Skip to content
Merged
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
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ implement, because they are distributed in their own right:
| `myna` | [mynaposter.com](https://mynaposter.com) — post, schedule and read across 25 social networks from a TUI |
| `devdb` | [terrablue/devdb](https://github.com/terrablue/devdb) — spin up a throwaway local database for development or testing |
| `kali` | [`@profullstack/kali`](https://github.com/profullstack/kali) — install a Kali-style web pentesting toolbelt on Debian/Ubuntu |
| `telnyx` | [`@telnyx/api-cli`](https://github.com/team-telnyx/telnyx-api-cli) — buy and wire phone numbers, send messages and drive calls |

And one group that is installed only when you ask for it, with
`cli-tools companions --install mobile`:
Expand All @@ -118,8 +119,8 @@ under any agentic CLI, from a Dockerfile, on a box that has never heard of this
repository. Vendoring them to make one list tidier would cost them all of that.
So `cli-tools` is their front door, not their implementation.

Six come from npm. `bw` was the first companion that is nobody's but its
vendor's — `adb`, `expo` and `eas` are the others — and it earns the place on
Seven come from npm. `bw` was the first companion that is nobody's but its
vendor's — `adb`, `expo`, `eas` and `telnyx` are the others — and it earns the place on
the same terms as the rest: published, self-installing, useful on a box with no
checkout. It covers the secrets `cli-tools` deliberately does not — the `vault` helpers read a logicsrc
team vault of shared API keys, which is a different thing from one person's
Expand Down Expand Up @@ -157,6 +158,20 @@ tool, so there is no need to run Kali itself. The tools are dual-use, which is
the point of keeping it a front door: it equips a box you are authorized to
test, and nothing about a target lives in this repository.

`telnyx` is here because the alternative is a web portal, and a phone number
bought by clicking is a number nobody can reproduce. `telnyx number search
--type toll_free`, `telnyx number order`, `telnyx call dial` — standing a
number up, and wiring it to something, as commands you can put in a script.
Note which package it is: Telnyx publishes two CLIs and both put `telnyx` on
PATH, so this is a choice about which rather than a chance to have both.
`@telnyx/cli` is generated from their OpenAPI spec over the whole API surface
and its own repository documents `go install` as the way to get it;
`@telnyx/api-cli` is the curated Node one, published to npm as a first-class
artifact, and its commands read like a person's. Like `myna` and `eas` it stays
a front door: `telnyx auth setup` writes the key to `~/.config/telnyx`,
`TELNYX_API_KEY` covers a box where an interactive prompt is not on offer, and
nothing about the account lives here.

`adb` is the odd one, and the reason there is a fourth kind of install. It is
not on npm — what is published under those names is either a Node
reimplementation of the wire protocol or somebody's mirror of Google's zip, and
Expand Down
22 changes: 22 additions & 0 deletions src/companions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,28 @@ export const COMPANIONS: readonly Companion[] = [
summary: 'Install a Kali-style web pentesting toolbelt on Debian/Ubuntu',
home: 'https://github.com/profullstack/kali',
},
{
name: 'telnyx',
// Phone numbers, messages and calls from the terminal, which is the point:
// the alternative is the Mission Control portal, and a number bought by
// clicking is a number nobody can reproduce. `telnyx number search
// --type toll_free`, `telnyx number order`, `telnyx call dial` -- the
// things you actually do to stand a phone number up.
//
// `@telnyx/api-cli`, not `@telnyx/cli`, and they are both Telnyx's. The
// latter is Stainless-generated over the whole API surface and its own
// repository documents `go install` as the way to get it; this one is the
// curated Node CLI, published to npm as a first-class artifact, and it is
// the one whose commands read like a person's. Both put `telnyx` on PATH,
// so this is a choice about which, not a chance to have both.
//
// A front door like `myna` and `eas`: `telnyx auth setup` writes the key
// to ~/.config/telnyx, and TELNYX_API_KEY works for a box where an
// interactive prompt is not on offer. Nothing about the account lives here.
install: { kind: 'npm', package: '@telnyx/api-cli' },
summary: 'Buy and wire phone numbers, send messages and drive calls on Telnyx',
home: 'https://github.com/team-telnyx/telnyx-api-cli',
},
{
name: 'adb',
// The Android Debug Bridge, and `fastboot` out of the same archive: one
Expand Down
9 changes: 6 additions & 3 deletions test/companions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ describe('the companion list', () => {
'myna',
'devdb',
'kali',
'telnyx',
'adb',
'expo',
'eas',
Expand Down Expand Up @@ -167,6 +168,7 @@ describe('statuses', () => {
['myna', 'missing'],
['devdb', 'missing'],
['kali', 'missing'],
['telnyx', 'missing'],
['adb', 'missing'],
['expo', 'missing'],
['eas', 'missing'],
Expand All @@ -182,7 +184,7 @@ describe('ensure', () => {
// Reinstalling over it is the surprise `link` refuses for symlinks.
const calls: string[] = [];
const results = ensure({
onPath: present('timer', 'billing', 'bw', 'diskpush', 'myna', 'devdb', 'kali'),
onPath: present('timer', 'billing', 'bw', 'diskpush', 'myna', 'devdb', 'kali', 'telnyx'),
run: ({ display }) => {
calls.push(display);
return { status: 0 };
Expand All @@ -194,7 +196,7 @@ describe('ensure', () => {

it('installs only what is missing', () => {
const calls: string[] = [];
const installed = new Set<string>(['timer', 'bw', 'diskpush', 'myna', 'devdb', 'kali']);
const installed = new Set<string>(['timer', 'bw', 'diskpush', 'myna', 'devdb', 'kali', 'telnyx']);
ensure({
onPath: (name) => (installed.has(name) ? `/usr/local/bin/${name}` : null),
run: ({ display }) => {
Expand All @@ -209,7 +211,7 @@ describe('ensure', () => {
it('reinstalls everything at @latest when asked', () => {
const calls: string[] = [];
ensure({
onPath: present('timer', 'billing', 'bw', 'diskpush', 'myna', 'devdb', 'kali'),
onPath: present('timer', 'billing', 'bw', 'diskpush', 'myna', 'devdb', 'kali', 'telnyx'),
run: ({ display }) => {
calls.push(display);
return { status: 0 };
Expand All @@ -226,6 +228,7 @@ describe('ensure', () => {
// Same reason, one step further: `go install` has no bare form to add to.
'go install github.com/terrablue/devdb@latest',
'npm install -g @profullstack/kali@latest',
'npm install -g @telnyx/api-cli@latest',
]);
});

Expand Down
Loading