Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@useswarm/cli

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@useswarm/cli - npm Package Compare versions

Comparing version
0.3.4
to
0.3.5
+41
-2
dist/commands/test.js

@@ -76,2 +76,4 @@ import { Command } from "commander";

.option("--cookies-stdin", "Read cookies JSON from stdin (for scripting)")
.option("--signup-email <email>", "Base email for sign-up flow — each persona gets a unique +<hash>@ alias")
.option("--signup-password <password>", "Optional shared password for sign-up forms (insecure — prefer interactive prompt). If omitted, the runtime generates a random per-persona password.")
.option("--start-url <url>", "URL the agent lands on after auth (recommended with --cookies — the post-auth dashboard)")

@@ -206,2 +208,4 @@ .option("--max-steps <n>", "Maximum steps per agent (default: 30)", "30")

let startUrl = opts.startUrl;
let signupEmail = opts.signupEmail;
let signupPassword = opts.signupPassword;
if (opts.passwordStdin) {

@@ -246,7 +250,10 @@ const chunks = [];

}
if (!authUsername && !authPassword && !cookies && interactive) {
const wantAuth = await promptConfirm(chalk.bold("\n Does this test require authentication?"), false);
if (!authUsername && !authPassword && !cookies && !signupEmail && interactive) {
// Auto-suggest sign-up mode when the goal text describes one.
const looksLikeSignupGoal = /\b(sign[\s-]?up|signup|register|create (?:an? )?(?:new )?account|new account|onboard(?:ing)?)\b/i.test(goal);
const wantAuth = await promptConfirm(chalk.bold("\n Does this test require authentication?"), looksLikeSignupGoal);
if (wantAuth) {
const authChoice = await promptSelect(chalk.bold(" Auth method:"), [
{ label: "Login with username + password", value: "login" },
{ label: "Sign-up — generate a unique +<hash> alias per persona from one base email", value: "signup" },
{ label: "Inject session cookies (skip login)", value: "cookies" },

@@ -280,2 +287,19 @@ ]);

}
else if (authChoice === "signup") {
signupEmail = await prompt(chalk.bold(" Base email ") + chalk.dim("(e.g. you@yourdomain.com): "));
if (!signupEmail || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(signupEmail.trim())) {
console.error(chalk.red("A valid base email is required for sign-up mode."));
process.exit(1);
}
signupEmail = signupEmail.trim();
// Show preview so users know what their personas will sign up with.
const [local, domain] = signupEmail.split("@");
const cleanLocal = local.split("+")[0];
console.log(chalk.dim(` → ${cleanLocal}+<unique-hash>@${domain} (one per persona)`));
const wantPwd = await promptConfirm(chalk.bold(" Provide a shared password for the sign-ups?") + chalk.dim(" (No = generate per-persona random)"), false);
if (wantPwd) {
signupPassword = await promptPassword(chalk.bold(" Password: "));
}
startUrl = startUrl || await prompt(chalk.bold(" Sign-up URL ") + chalk.dim("(optional — where the form lives): ")) || undefined;
}
else {

@@ -330,2 +354,8 @@ loginUrl = await prompt(chalk.bold(" Login page URL ") + chalk.dim("(optional, press Enter to skip)") + chalk.bold(": ")) || undefined;

}
else if (signupEmail) {
const [local, domain] = signupEmail.split("@");
const cleanLocal = local.split("+")[0];
console.log(` Auth: ${chalk.green("signup")} (${cleanLocal}+<hash>@${domain})`);
console.log(` Password: ${signupPassword ? chalk.dim("(shared, provided)") : chalk.dim("(per-persona random, not saved)")}`);
}
if (startUrl) {

@@ -527,2 +557,11 @@ console.log(` Start URL: ${chalk.cyan(startUrl)}`);

}
else if (signupEmail) {
// Sign-up mode — base email is fanned out into unique +<hash>@ aliases per persona.
body.auth = {
mode: "signup",
signupEmail,
...(signupPassword ? { signupPassword } : {}),
...(startUrl ? { startUrl } : {}),
};
}
else if (authUsername && authPassword) {

@@ -529,0 +568,0 @@ body.auth = {

#!/usr/bin/env node
import { Command } from "commander";
import chalk from "chalk";
import { readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { loginCommand, logoutCommand } from "./commands/login.js";

@@ -9,2 +12,4 @@ import { testCommand } from "./commands/test.js";

import { getConfig } from "./lib/config.js";
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
const { version: pkgVersion } = JSON.parse(readFileSync(pkgPath, "utf8"));
const program = new Command();

@@ -14,3 +19,3 @@ program

.description("Swarm CLI — run AI-powered UX simulations from the terminal")
.version("0.3.0");
.version(pkgVersion);
program.addCommand(loginCommand);

@@ -17,0 +22,0 @@ program.addCommand(logoutCommand);

@@ -46,4 +46,10 @@ export interface InitiateResponse {

auth?: {
/** "agent_login" (username + password) or "cookie_injection" (session cookies). Defaults to agent_login server-side. */
mode?: "agent_login" | "cookie_injection";
/** Auth flow:
* - "agent_login": username + password against an existing account
* - "cookie_injection": inject session cookies, skip login
* - "signup": no account yet — agent fills sign-up form using a unique
* sub-aliased email (`local+<hash>@domain`) per persona derived from
* one base email the user supplies.
* Defaults to "agent_login" server-side. */
mode?: "agent_login" | "cookie_injection" | "signup";
/** URL to land on after auth — recommended for cookie_injection (the post-auth dashboard). */

@@ -63,2 +69,6 @@ startUrl?: string;

}>;
signupEmail?: string;
/** Optional shared password. If omitted, the runtime mints a strong random
* one per persona-run (not persisted). */
signupPassword?: string;
};

@@ -65,0 +75,0 @@ }

+8
-7
{
"name": "@useswarm/cli",
"version": "0.3.4",
"version": "0.3.5",
"description": "Swarm CLI — AI-powered UX testing from your terminal",

@@ -13,2 +13,8 @@ "type": "module",

],
"scripts": {
"dev": "tsx src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"prepublishOnly": "npm run build"
},
"keywords": [

@@ -44,8 +50,3 @@ "ux",

"node": ">=18"
},
"scripts": {
"dev": "tsx src/index.ts",
"build": "tsc",
"start": "node dist/index.js"
}
}
}

@@ -107,2 +107,6 @@ # @useswarm/cli

Three modes — pick the one that matches what your test needs.
**Login mode** (existing account):
| Flag | Description |

@@ -115,2 +119,17 @@ |------|-------------|

**Sign-up mode** (no account yet — each persona gets a unique sub-aliased email):
| Flag | Description |
|------|-------------|
| `--signup-email <email>` | Base email — each persona registers as `local+<hash>@domain` derived from this |
| `--signup-password <pass>` | Optional shared password. If omitted, a strong random password is generated per persona-run (not persisted) |
**Cookie injection** (skip login entirely):
| Flag | Description |
|------|-------------|
| `--cookies <file>` | Path to a JSON file with session cookies |
| `--cookies-stdin` | Read cookies JSON from stdin |
| `--start-url <url>` | URL the agent lands on after auth (recommended with `--cookies`) |
The interactive prompt masks password input. For scripts:

@@ -122,2 +141,4 @@

When you run `swarm test` interactively without auth flags, it offers all three modes — and auto-suggests **Sign-up** when your goal text mentions "sign up", "register", "create account", or "onboard".
#### Other

@@ -225,4 +246,6 @@

Test flows that require login:
Three modes are supported.
#### Login (existing account)
```bash

@@ -235,2 +258,39 @@ swarm test --url localhost:3000 --login-url /login --username test@example.com

#### Sign-up (one base email, N unique aliases)
For testing sign-up flows: you supply one base email, each persona registers with a unique sub-aliased address derived from it.
```bash
swarm test --url localhost:3000 --goal "Sign up for an account" \
--signup-email you@yourdomain.com \
--agents 5
# Personas register as:
# you+lwk3a8x7@yourdomain.com
# you+lwk3a902@yourdomain.com
# you+lwk3a9f1@yourdomain.com
# ...
# All confirmation emails arrive in your one inbox at you@yourdomain.com.
```
Each alias is generated at run time as `local+<base36(timestamp)+random>@domain`:
- The timestamp half guarantees the alias can never be replicated in the future.
- The random half disambiguates personas spawned in the same millisecond.
- The agent is told to type the exact alias verbatim — the standard login pre-fill and login tool are suppressed for sign-up runs (the agent fills the form itself).
- The generated alias is persisted to the run record so confirmation emails can be tied back to a specific persona.
Pass `--signup-password` to share one password across all personas; omit it to have the runtime mint a strong random password per persona-run (not persisted).
Plus-addressing works with Gmail, Fastmail, and most custom domains.
#### Cookie injection (skip login)
```bash
swarm test --url localhost:3000 --goal "Explore settings" \
--cookies ./cookies.json \
--start-url http://localhost:3000/dashboard
```
Capture the cookies file with the [Cookie-Editor](https://chromewebstore.google.com/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) browser extension — Export → JSON. When testing localhost, cookie domains captured from a different host are auto-rewritten to match the tunnel.
## CI/CD Usage

@@ -237,0 +297,0 @@