
Security News
Another Round of TEA Protocol Spam Floods npm, But It’s Not a Worm
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.
 
An opinionated framework for creating modern CLI applications in Node.js, with a simple and composable approach to command definitions.
Whereas most CLI frameworks use the builder pattern for defining the behavior of the application ts-argue defines a pattern for a command object. These commands can then be composed into a tree of sub-commands to define your application. In addition to defining the application behavior this tree of commands auto-generates contextual help text and suggestions.
As an example lets create a simple CRUD application that stores contacts. We will have 5 sub-commands: create, read, list, update and delete. If we don't specify a sub-command we will list the contacts. This is what our application will look like.
> contacts create 'Jane Doe' --mobile '01234 567890' --email 'janedoe@unknown.com'
Created new entry for 'Jane Doe'
> contacts read 'Jane Doe'
Name: Jane Doe
Mobile: 01234 567890
Email: janedoe@unknown.com
> contacts list
Contacts
- Jane Doe
> contacts
Contacts
- Jane Doe
> contacts update 'Jane Doe' --name 'Jenny Doe' --mobile '01111 567890' --email 'jennydoe@unknown.com'
Updated contact details for 'Jane Doe'
Name: Jenny Doe
Mobile: 01111 567890
Email: jennydoe@unknown.com
> contacts delete 'Jenny Doe'
Deleted contact 'Jenny Doe'
import type { Argv, Command } from 'ts-argue';
import type { Contact } from './database';
// our actual storage and persistence will be dealt with outside of our UI layer
import { contact_database } from './database';
import { read_string_option, run_command, terminal, style } from 'ts-argue';
// first we define each sub-command
// the create command has a few options, and requires an argument
const create_command: Command = {
description: 'Create a new contact',
options: {
name: 'The name of the contact',
mobile: 'The mobile number of the contact',
email: 'The email address of the contact',
},
async action(opts) {
const name = get_name_argument(opts);
const { mobile, email } = read_standard_options(opts);
await contact_database.insert({ name, mobile, email });
terminal.print_line(`Created new entry for ${style.bold(name)}`);
}
};
// the read command has no options, and requires an argument
const read_command: Command = {
description: 'Read the details of a specific contact',
async action(opts) {
const name = get_name_argument(opts);
const entry = await get_existing_contact(name);
print_contact(entry);
}
};
// the list command has no options, and take no arguments
const list_command: Command = {
description: 'List all your contacts',
async action() {
const entries = await contact_database.entries();
if (entries.length === 0) {
terminal
.print_line('No contacts found in database.')
.new_line()
.print_line('To learn how to create a new contact try:')
.increase_indent()
.print_line(style.bold('contacts create help'))
.new_line();
return;
}
terminal
.print_line(style.bold`CONTACTS:`)
.increase_indent();
for (const entry of entries) {
terminal.print_line(entry.name);
}
}
};
// the update command has a few options, and requires an argument
const update_command: Command = {
description: 'Update the details for a contact',
options: {
name: 'A new name for the contact',
mobile: 'A new mobile number of the contact',
email: 'A new email address of the contact',
},
async action(opts) {
const name = get_name_argument(opts);
const { name: new_name, mobile, email } = read_standard_options(opts);
const entry = await get_existing_contact(name);
if (new_name) {
await contact_database.delete(name);
}
const new_entry = {
name: new_name || name,
email: email || entry.email,
mobile: mobile || entry.mobile,
};
await contact_database.insert(new_entry);
terminal.print_line(`Updated contact details for ${style.bold(new_entry.name)}`);
print_contact(new_entry);
}
};
// the delete command has no options, and requires an argument
const delete_command: Command = {
description: 'Delete a contact',
async action(opts) {
const name = get_name_argument(opts);
await get_existing_contact(name);
await contact_database.delete(name);
terminal.print_line(`Deleted contact ${style.bold(name)}`);
}
};
const get_name_argument = (opts: Argv) => {
const name = opts.arguments[0];
if (!name) {
throw new Error('Expected a name');
}
return name;
};
const get_existing_contact = async (name: string) => {
const entry = await contact_database.get(name);
if (!entry) {
throw new Error(`No entry found for '${name}'`);
}
return entry;
};
const read_standard_options = (opts: Argv) => {
return {
name: read_string_option(opts, 'name', ''),
mobile: read_string_option(opts, 'mobile', ''),
email: read_string_option(opts, 'email', ''),
};
};
const print_contact = ({ name, email, mobile }: Contact) => {
terminal.print_lines([
`${style.dim('Name:')} ${name}`,
`${style.dim('Mobile:')} ${mobile ?? ''}`,
`${style.dim('Email:')} ${email ?? ''}`
]);
};
// next we define our "root" command
// we specify our other commands as sub-commands, and indicate the default
// subcommand is 'list'
const root_command: Command = {
description: 'My amazing contacts app',
subcommands: {
create: create_command,
read: read_command,
list: list_command,
update: update_command,
delete: delete_command
},
default: 'list'
};
// finally to actually execute our application we call run_command with our
// root command and our arguments. there is no need to trim the arguments
void run_command(root_command, { version: '1' });
You can try the above example by downloading this repo, installing the dev dependencies and running npm run example ( this uses ts-node so there is a bit of a delay which wouldn't be present in a production application ).
This library has only been used for proof of concepts at the moment, and is not considered complete. It is being developed side by side another project, and the aim is to produce a 1.0.0 release around the time that project is complete.
FAQs
 
The npm package ts-argue receives a total of 0 weekly downloads. As such, ts-argue popularity was classified as not popular.
We found that ts-argue demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.

Security News
PyPI adds Trusted Publishing support for GitLab Self-Managed as adoption reaches 25% of uploads

Research
/Security News
A malicious Chrome extension posing as an Ethereum wallet steals seed phrases by encoding them into Sui transactions, enabling full wallet takeover.