sella-cli 0.6.4 → 0.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +205 -37
- package/dist/index.js +31 -3
- package/dist/telemetry-send.js +18 -0
- package/dist/telemetry.js +153 -0
- package/package.json +21 -2
package/README.md
CHANGED
|
@@ -1,51 +1,219 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://sellag.vercel.app"><img src="https://sellag.vercel.app/sellalogo.png" alt="Sella" width="320" /></a>
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
<h1 align="center">sella-cli</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<b>Your AI agent can buy the data and APIs it needs. On its own, paid per call, in USDC.<br/>
|
|
9
|
+
This CLI connects it in one command.</b>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://www.npmjs.com/package/sella-cli"><img src="https://img.shields.io/npm/v/sella-cli?color=E84B2A&label=npm" alt="npm version" /></a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/sella-cli"><img src="https://img.shields.io/npm/dm/sella-cli?color=555" alt="npm downloads" /></a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/sella-cli"><img src="https://img.shields.io/node/v/sella-cli?color=339933&label=node" alt="node version" /></a>
|
|
16
|
+
<a href="https://github.com/010100100100011101010100/ogsella/issues"><img src="https://img.shields.io/npm/l/sella-cli?color=blue" alt="license" /></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="https://sellag.vercel.app">Website</a> ·
|
|
21
|
+
<a href="https://sella.mintlify.app">Docs</a> ·
|
|
22
|
+
<a href="https://sellag.vercel.app/marketplace">Marketplace</a> ·
|
|
23
|
+
<a href="https://github.com/010100100100011101010100/ogsella/issues">Report a bug</a>
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Connect your agent in about 30 seconds
|
|
5
29
|
|
|
6
30
|
```bash
|
|
7
31
|
npx sella-cli
|
|
8
32
|
```
|
|
9
33
|
|
|
10
|
-
That
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
34
|
+
That one command is the whole setup. The wizard:
|
|
35
|
+
|
|
36
|
+
1. **Detects your agent clients**: Claude Code, Claude Desktop, Cursor, Windsurf, VS Code, Cline.
|
|
37
|
+
2. **Installs the Sella MCP server** into each one. Writes are idempotent and your other MCP servers are preserved.
|
|
38
|
+
3. **Pairs the machine**: paste a setup code from the dashboard, or use your email and a 6-digit code.
|
|
39
|
+
4. **Proves it works** with a live `sella doctor` run at the end.
|
|
40
|
+
|
|
41
|
+
<p align="center">
|
|
42
|
+
<img src="https://sellag.vercel.app/readme/cli-init.svg" alt="npx sella-cli: the wizard detects clients, installs the Sella MCP server, pairs the machine, and verifies with 6/6 checks" width="740" />
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
When it finishes, your agent can search a live marketplace and pay for what it finds, per call, in USDC. You set the budget. You can revoke the key anytime.
|
|
46
|
+
|
|
47
|
+
## Why this exists
|
|
48
|
+
|
|
49
|
+
An agent can only use what a human wired up for it in advance. The moment it needs one more API mid-task, it stops and waits for you. Then you do this, again:
|
|
50
|
+
|
|
51
|
+
<p align="center">
|
|
52
|
+
<img src="https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif" alt="A cat typing frantically on a laptop" width="360" /><br/>
|
|
53
|
+
<sub><i>You, wiring up the seventh API key this month.</i></sub>
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
| The traditional way | With Sella |
|
|
57
|
+
|---|---|
|
|
58
|
+
| Find the API. Make an account. Add a card. Copy the key. Paste it into a config. Restart the client. Repeat for every provider. | Your agent searches the marketplace, pays per call in USDC, and keeps working. No account per provider, no key to paste. |
|
|
59
|
+
| Integration time: an evening, per API. | `npx sella-cli`, once. |
|
|
60
|
+
| The agent waits for you. | You set a budget and walk away. |
|
|
61
|
+
|
|
62
|
+
## Try it free, right now (no account)
|
|
63
|
+
|
|
64
|
+
The sandbox searches the real marketplace. No signup, rate-limited:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx sella-cli sandbox "web search"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
<p align="center">
|
|
71
|
+
<img src="https://sellag.vercel.app/readme/cli-sandbox.svg" alt="sella sandbox returns live marketplace results: Exa, Tavily and more, with no account" width="740" />
|
|
72
|
+
</p>
|
|
73
|
+
|
|
74
|
+
What's in there: a catalogue of 1,100+ machine-payable API providers, plus first-party datasets, workflows, and Sella Native products such as Cradle. Free previews let your agent check quality before it pays.
|
|
75
|
+
|
|
76
|
+
## What you get
|
|
77
|
+
|
|
78
|
+
- **One-command onboarding**, so you never hand-edit an MCP config again.
|
|
79
|
+
- **Pay-per-call in USDC**: fractions of a cent for most calls, multi-chain, with budgets you set and keys you can revoke.
|
|
80
|
+
- **A marketplace built for agents**: every listing is discoverable and payable over MCP + x402, so your agent buys mid-task instead of stalling.
|
|
81
|
+
- **Publishing from a CSV**, so you can sell your own data without opening a browser.
|
|
82
|
+
- **Agent-grade output**: every command supports `--json`, `--yes`, and env vars, with meaningful exit codes. Your CI can run it. Your agent can run it.
|
|
17
83
|
|
|
18
84
|
## Commands
|
|
19
85
|
|
|
20
86
|
| Command | What it does |
|
|
21
87
|
|---|---|
|
|
22
|
-
| `sella init` |
|
|
23
|
-
| `sella sandbox <query>` |
|
|
24
|
-
| `sella pair` | Pair this machine only
|
|
25
|
-
| `sella clients` | List detected clients
|
|
26
|
-
| `sella doctor` |
|
|
27
|
-
| `sella status` |
|
|
28
|
-
| `sella fund` | Per-chain USDC deposit addresses
|
|
29
|
-
| `sella mcp` | Run
|
|
30
|
-
| `sella publish init <file.csv>` | Scaffold `sella-dataset.json`
|
|
31
|
-
| `sella publish push` | Publish the dataset
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
88
|
+
| `sella init` | The guided wizard: detect clients, install the Sella MCP server, pair, verify. Also runs on a bare `sella` when no credentials exist yet. |
|
|
89
|
+
| `sella sandbox <query>` | Search the live marketplace with no account. Rate-limited. |
|
|
90
|
+
| `sella pair` | Pair this machine only. Use `--setup-code <code>`, the `SELLA_SETUP_CODE` env var, or `--email` plus a 6-digit code. |
|
|
91
|
+
| `sella clients` | List detected agent clients. `--install` writes configs, `--client a,b` filters, `--dry-run` previews. |
|
|
92
|
+
| `sella doctor` | Six live checks, from endpoint reachability to a real x402 pay quote. Every failure names its fix. |
|
|
93
|
+
| `sella status` | Your API key and agent wallet balances, each chain labelled live or deposit-only. |
|
|
94
|
+
| `sella fund` | Per-chain USDC deposit addresses, plus the funding page with QR codes and a fiat on-ramp. |
|
|
95
|
+
| `sella mcp` | Run a local stdio MCP server that proxies Sella with your stored key, for clients that cannot use a remote endpoint. |
|
|
96
|
+
| `sella publish init <file.csv>` | Scaffold `sella-dataset.json` and pre-check the CSV structure. |
|
|
97
|
+
| `sella publish push` | Publish the dataset through the same route the web dashboard uses. |
|
|
98
|
+
|
|
99
|
+
Run `sella --help` for every flag.
|
|
100
|
+
|
|
101
|
+
## Examples
|
|
102
|
+
|
|
103
|
+
### Small: search the marketplace (10 seconds, free)
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npx sella-cli sandbox "flight prices"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Real listings, real prices, no account.
|
|
110
|
+
|
|
111
|
+
### Medium: connect Claude Code and let the agent do the buying
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npx sella-cli # detect, install, pair, verify
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Then ask your agent:
|
|
118
|
+
|
|
119
|
+
> Search Sella for a web search API and tell me what it costs per call.
|
|
120
|
+
|
|
121
|
+
The agent answers using the Sella MCP tools (`search_datasets`, `list_market_apis`, `try_dataset`, and friends) with the key this CLI stored for it. When you fund the agent wallet, it can buy what it found.
|
|
122
|
+
|
|
123
|
+
### Real-world: publish a CSV and get paid in USDC
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
sella publish init leads.csv # scaffolds sella-dataset.json + structural pre-check
|
|
127
|
+
# edit title, description, price in sella-dataset.json
|
|
128
|
+
sella publish push # same route the web dashboard uses
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Your CSV becomes a listing. Once it clears review it is live for every MCP agent to find, preview, and buy. You are paid per access.
|
|
132
|
+
|
|
133
|
+
### Ready-made agent tasks
|
|
134
|
+
|
|
135
|
+
The [RFI gallery](https://sellag.vercel.app/rfi) is a list of ready-to-send prompts for your newly connected agent:
|
|
136
|
+
|
|
137
|
+
- [Validate a startup idea for $25](https://sellag.vercel.app/rfi?uc=twenty-five-dollar-study)
|
|
138
|
+
- [Get a daily morning brief](https://sellag.vercel.app/rfi?uc=morning-brief)
|
|
139
|
+
- [Test Sella without spending anything](https://sellag.vercel.app/rfi?uc=zero-dollar-demo)
|
|
140
|
+
|
|
141
|
+
## Something broke?
|
|
142
|
+
|
|
143
|
+
<p align="center">
|
|
144
|
+
<img src="https://media.giphy.com/media/13HgwGsXF0aiGY/giphy.gif" alt="Moss from The IT Crowd typing calmly while a fire burns beside him" width="360" /><br/>
|
|
145
|
+
<sub><i>Debugging by vibes.</i></sub>
|
|
146
|
+
</p>
|
|
147
|
+
|
|
148
|
+
Don't. Run the doctor:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
sella doctor
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
<p align="center">
|
|
155
|
+
<img src="https://sellag.vercel.app/readme/cli-doctor.svg" alt="sella doctor: six checks pass, from MCP endpoint reachability to a live x402 pay quote" width="740" />
|
|
156
|
+
</p>
|
|
157
|
+
|
|
158
|
+
Each failed check prints what happened and the exact fix, in plain language. If it still looks wrong after that, [open an issue](https://github.com/010100100100011101010100/ogsella/issues) and paste the `sella doctor --json` output.
|
|
159
|
+
|
|
160
|
+
## Built for agents and CI, not just your terminal
|
|
161
|
+
|
|
162
|
+
- `--json` prints exactly one JSON document on stdout. Errors go to stderr. Exit codes mean something.
|
|
163
|
+
- `--yes` and env vars cover every prompt. Nothing hangs waiting for a TTY.
|
|
164
|
+
- `NO_COLOR` and `--no-color` are honored. Color never carries meaning alone.
|
|
165
|
+
- Config writes are idempotent, preserve your other MCP servers, and leave a one-time `.sella-backup`.
|
|
166
|
+
- `SELLA_MCP_URL` points the CLI at staging or a self-hosted Sella.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# Headless pairing in CI
|
|
170
|
+
SELLA_SETUP_CODE=SELLA-XXXX-XXXX-XXXX-XXXX npx sella-cli init --yes --json
|
|
171
|
+
```
|
|
46
172
|
|
|
47
173
|
## Security
|
|
48
174
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
175
|
+
- Configs written before pairing contain **no secrets**. Sella's bootstrap MCP tools are callable without a token, so the agent pairs itself and stores its own credentials.
|
|
176
|
+
- Your API key lands in the OS keychain, or in a `0600` file with `--no-keychain`.
|
|
177
|
+
- You set the budget. You can revoke a key from the dashboard at any time.
|
|
178
|
+
- Never paste API keys into URLs or shell history.
|
|
179
|
+
|
|
180
|
+
## Telemetry (opt-in, off by default)
|
|
181
|
+
|
|
182
|
+
The CLI can count which commands run and whether they exit cleanly, so we know what to fix first. Nothing is sent unless you say yes to the one-time question at the end of `sella init`, or you set `SELLA_TELEMETRY=1`.
|
|
183
|
+
|
|
184
|
+
When enabled, each command sends one event: the command name (like `doctor`), the exit code, the CLI version, the OS name (like `darwin`), and a random install id. Never sent: search queries, emails, API keys, wallet addresses, file paths, or anything else you typed. Telemetry is always skipped in `--json`, `--yes`, and CI runs, and the send happens in a detached process, so it never delays a command.
|
|
185
|
+
|
|
186
|
+
Opt out anytime with `SELLA_TELEMETRY=0` or by deleting `~/.sella/telemetry.json`.
|
|
187
|
+
|
|
188
|
+
## FAQ
|
|
189
|
+
|
|
190
|
+
**Is this crypto hype?**
|
|
191
|
+
No. Payments settle in USDC, a dollar-pegged stablecoin, and most calls cost fractions of a cent. It works with the agent clients you already use.
|
|
192
|
+
|
|
193
|
+
**Do I have to rebuild my stack?**
|
|
194
|
+
No. It is one MCP server entry in a config you already have, and this CLI writes it for you. Try the sandbox before you even sign up.
|
|
195
|
+
|
|
196
|
+
**Is my money safe?**
|
|
197
|
+
You fund a dedicated agent wallet, you set the budget, and you can revoke access anytime. Keys are stored encrypted.
|
|
198
|
+
|
|
199
|
+
**I don't like terminals.**
|
|
200
|
+
Sella has two other ways in: your agent can onboard itself over MCP with your email and a one-time code, or you sign up on the web and paste a setup code to your agent. This CLI is the third path, for people who live in a shell.
|
|
201
|
+
|
|
202
|
+
## Links
|
|
203
|
+
|
|
204
|
+
| | |
|
|
205
|
+
|---|---|
|
|
206
|
+
| Website | https://sellag.vercel.app |
|
|
207
|
+
| Docs | https://sella.mintlify.app |
|
|
208
|
+
| Marketplace | https://sellag.vercel.app/marketplace |
|
|
209
|
+
| Agent task ideas | https://sellag.vercel.app/rfi |
|
|
210
|
+
| npm | https://www.npmjs.com/package/sella-cli |
|
|
211
|
+
| Issues | https://github.com/010100100100011101010100/ogsella/issues |
|
|
212
|
+
|
|
213
|
+
## Found a bug?
|
|
214
|
+
|
|
215
|
+
Found a bug? Have an idea? [Mail us](rasesh@buildifi.ai). We read every one.
|
|
216
|
+
|
|
217
|
+
## License
|
|
218
|
+
|
|
219
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -11,8 +11,9 @@ import { getFundingInfo, annotateFunding } from './fund.js';
|
|
|
11
11
|
import { capabilityLabel } from './chains.js';
|
|
12
12
|
import { defaultIo, Printer } from './output.js';
|
|
13
13
|
import { Ui } from './ui.js';
|
|
14
|
+
import { recordCliEvent, saveTelemetryDecision, shouldPromptTelemetry } from './telemetry.js';
|
|
14
15
|
const DEFAULT_MCP_URL = 'https://sellag.vercel.app/api/mcp';
|
|
15
|
-
const VERSION = '0.6.
|
|
16
|
+
const VERSION = '0.6.6';
|
|
16
17
|
function parseFlags(argv) {
|
|
17
18
|
const flags = {
|
|
18
19
|
json: false, yes: false, noColor: false, dryRun: false, noKeychain: false,
|
|
@@ -82,6 +83,11 @@ Environment:
|
|
|
82
83
|
SELLA_MCP_URL Override the MCP endpoint (default ${DEFAULT_MCP_URL})
|
|
83
84
|
SELLA_SETUP_CODE Setup code for headless/CI pairing
|
|
84
85
|
SELLA_NO_KEYCHAIN Set to 1 to force file storage for the API key
|
|
86
|
+
|
|
87
|
+
Links:
|
|
88
|
+
Website https://sellag.vercel.app
|
|
89
|
+
Docs https://sella.mintlify.app
|
|
90
|
+
Issues https://github.com/010100100100011101010100/ogsella/issues
|
|
85
91
|
`;
|
|
86
92
|
function selectClientIds(flags, detected) {
|
|
87
93
|
if (flags.clients) {
|
|
@@ -305,6 +311,16 @@ async function cmdInit(ctx, flags, printer) {
|
|
|
305
311
|
{ tag: 'free', text: `Test Sella without spending — ${rfi}?uc=zero-dollar-demo`, tone: 'dim' },
|
|
306
312
|
]);
|
|
307
313
|
ui.detail(`Browse all ideas: ${rfi}`);
|
|
314
|
+
// One-time telemetry choice: only a human at a TTY is ever asked, and only once.
|
|
315
|
+
if (!flags.dryRun && shouldPromptTelemetry({ env: ctx.env, ioEnv: ctx.io.env, interactive: ui.interactive })) {
|
|
316
|
+
ui.bar();
|
|
317
|
+
const share = await ui.select('Help improve this CLI with anonymous usage stats?', [
|
|
318
|
+
{ value: 'share', label: 'Share stats', hint: 'command name and pass or fail only, never your data or keys' },
|
|
319
|
+
{ value: 'off', label: 'Keep it off', hint: 'nothing is sent' },
|
|
320
|
+
]);
|
|
321
|
+
saveTelemetryDecision(ctx.env, share === 'share');
|
|
322
|
+
ui.detail(share === 'share' ? 'Thanks. Opt out anytime: SELLA_TELEMETRY=0' : 'Nothing will be sent. Opt in later: SELLA_TELEMETRY=1');
|
|
323
|
+
}
|
|
308
324
|
const took = Math.max(1, Math.round((Date.now() - startedAt) / 1000));
|
|
309
325
|
ui.outro(flags.dryRun ? 'Dry run complete — nothing was written.' : `Done in ${took}s — your agents can now buy on Sella.`);
|
|
310
326
|
}
|
|
@@ -321,6 +337,9 @@ export async function runCli(argv, io = defaultIo(), env = defaultEnv()) {
|
|
|
321
337
|
const command = flags.positional[0];
|
|
322
338
|
if (flags.version) {
|
|
323
339
|
io.stdout(VERSION);
|
|
340
|
+
// TTY-only: scripts capturing `sella --version` must still get a bare semver.
|
|
341
|
+
if (io.isTTY)
|
|
342
|
+
io.stdout('docs: https://sella.mintlify.app · issues: https://github.com/010100100100011101010100/ogsella/issues');
|
|
324
343
|
return 0;
|
|
325
344
|
}
|
|
326
345
|
if (flags.help) {
|
|
@@ -548,8 +567,17 @@ export async function runCli(argv, io = defaultIo(), env = defaultEnv()) {
|
|
|
548
567
|
// Bin entry (skipped when imported by tests).
|
|
549
568
|
const isMain = process.argv[1]?.endsWith('index.js') || process.argv[1]?.endsWith('sella');
|
|
550
569
|
if (isMain) {
|
|
551
|
-
|
|
552
|
-
|
|
570
|
+
const argv = process.argv.slice(2);
|
|
571
|
+
runCli(argv)
|
|
572
|
+
.then((code) => {
|
|
573
|
+
try {
|
|
574
|
+
recordCliEvent({ argv, exitCode: code, version: VERSION, env: defaultEnv(), ioEnv: process.env });
|
|
575
|
+
}
|
|
576
|
+
catch {
|
|
577
|
+
// Telemetry must never affect the exit path.
|
|
578
|
+
}
|
|
579
|
+
process.exit(code);
|
|
580
|
+
})
|
|
553
581
|
.catch((err) => {
|
|
554
582
|
console.error(err instanceof Error ? err.message : String(err));
|
|
555
583
|
process.exit(1);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Detached one-shot telemetry sender: the CLI spawns this and exits immediately, so a slow or
|
|
2
|
+
// dead network can never delay a command. argv carries no secrets (see telemetry.ts payload).
|
|
3
|
+
const [, , url, body] = process.argv;
|
|
4
|
+
if (url && body) {
|
|
5
|
+
try {
|
|
6
|
+
await fetch(url, {
|
|
7
|
+
method: 'POST',
|
|
8
|
+
headers: { 'content-type': 'application/json' },
|
|
9
|
+
body,
|
|
10
|
+
signal: AbortSignal.timeout(4000),
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// Best-effort by contract: there is nobody to report to.
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
process.exit(0);
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
/** Mirrored by CLI_TELEMETRY_COMMANDS in lib/telemetry/cli-event.ts (server side). */
|
|
7
|
+
export const TELEMETRY_COMMANDS = [
|
|
8
|
+
'none',
|
|
9
|
+
'init',
|
|
10
|
+
'pair',
|
|
11
|
+
'sandbox',
|
|
12
|
+
'clients',
|
|
13
|
+
'doctor',
|
|
14
|
+
'status',
|
|
15
|
+
'fund',
|
|
16
|
+
'mcp',
|
|
17
|
+
'publish',
|
|
18
|
+
'publish:init',
|
|
19
|
+
'publish:push',
|
|
20
|
+
'version',
|
|
21
|
+
'help',
|
|
22
|
+
'unknown',
|
|
23
|
+
];
|
|
24
|
+
const DEFAULT_ORIGIN = 'https://sellag.vercel.app';
|
|
25
|
+
/** Flags whose next token is a value, not a command (mirrors parseFlags in index.ts). */
|
|
26
|
+
const VALUE_FLAGS = new Set(['--client', '--clients', '--setup-code', '--email', '--card']);
|
|
27
|
+
export function telemetryPath(env) {
|
|
28
|
+
return path.join(env.home, '.sella', 'telemetry.json');
|
|
29
|
+
}
|
|
30
|
+
export function loadTelemetryDecision(env) {
|
|
31
|
+
try {
|
|
32
|
+
const raw = JSON.parse(fs.readFileSync(telemetryPath(env), 'utf8'));
|
|
33
|
+
if (typeof raw.enabled !== 'boolean')
|
|
34
|
+
return null;
|
|
35
|
+
return {
|
|
36
|
+
enabled: raw.enabled,
|
|
37
|
+
installId: typeof raw.installId === 'string' && raw.installId ? raw.installId : 'unset',
|
|
38
|
+
decidedAt: typeof raw.decidedAt === 'string' ? raw.decidedAt : '',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function saveTelemetryDecision(env, enabled) {
|
|
46
|
+
const file = telemetryPath(env);
|
|
47
|
+
const existing = loadTelemetryDecision(env);
|
|
48
|
+
const decision = {
|
|
49
|
+
enabled,
|
|
50
|
+
installId: existing?.installId && existing.installId !== 'unset' ? existing.installId : randomUUID(),
|
|
51
|
+
decidedAt: new Date().toISOString(),
|
|
52
|
+
};
|
|
53
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
54
|
+
fs.writeFileSync(file, `${JSON.stringify(decision, null, 2)}\n`);
|
|
55
|
+
return { ...decision, path: file };
|
|
56
|
+
}
|
|
57
|
+
/** SELLA_TELEMETRY=1 forces on, =0 forces off, anything else defers to the stored decision. */
|
|
58
|
+
export function telemetryEnvOverride(ioEnv) {
|
|
59
|
+
const v = (ioEnv.SELLA_TELEMETRY || '').trim();
|
|
60
|
+
if (v === '1')
|
|
61
|
+
return true;
|
|
62
|
+
if (v === '0')
|
|
63
|
+
return false;
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
export function isCiRun(ioEnv) {
|
|
67
|
+
const v = (ioEnv.CI || '').trim().toLowerCase();
|
|
68
|
+
return v !== '' && v !== '0' && v !== 'false';
|
|
69
|
+
}
|
|
70
|
+
/** The single yes/no the sender consults. Defaults to off in every ambiguous case. */
|
|
71
|
+
export function telemetryEnabled(env, ioEnv) {
|
|
72
|
+
if (isCiRun(ioEnv))
|
|
73
|
+
return false;
|
|
74
|
+
const override = telemetryEnvOverride(ioEnv);
|
|
75
|
+
if (override !== null)
|
|
76
|
+
return override;
|
|
77
|
+
return loadTelemetryDecision(env)?.enabled === true;
|
|
78
|
+
}
|
|
79
|
+
/** Ask only when a human is present, nothing decided yet, and no env var already speaks. */
|
|
80
|
+
export function shouldPromptTelemetry(opts) {
|
|
81
|
+
if (!opts.interactive)
|
|
82
|
+
return false;
|
|
83
|
+
if (isCiRun(opts.ioEnv))
|
|
84
|
+
return false;
|
|
85
|
+
if (telemetryEnvOverride(opts.ioEnv) !== null)
|
|
86
|
+
return false;
|
|
87
|
+
return loadTelemetryDecision(opts.env) === null;
|
|
88
|
+
}
|
|
89
|
+
/** Collapse argv onto the fixed command vocabulary; free text can never pass through. */
|
|
90
|
+
export function commandFromArgv(argv) {
|
|
91
|
+
if (argv.includes('--version') || argv.includes('-v'))
|
|
92
|
+
return 'version';
|
|
93
|
+
if (argv.includes('--help') || argv.includes('-h'))
|
|
94
|
+
return 'help';
|
|
95
|
+
const positional = [];
|
|
96
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
97
|
+
const arg = argv[i];
|
|
98
|
+
if (VALUE_FLAGS.has(arg)) {
|
|
99
|
+
i += 1;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (arg.startsWith('-'))
|
|
103
|
+
continue;
|
|
104
|
+
positional.push(arg);
|
|
105
|
+
}
|
|
106
|
+
const command = positional[0];
|
|
107
|
+
if (!command)
|
|
108
|
+
return 'none';
|
|
109
|
+
if (command === 'publish') {
|
|
110
|
+
const sub = positional[1];
|
|
111
|
+
if (sub === 'init')
|
|
112
|
+
return 'publish:init';
|
|
113
|
+
if (sub === 'push')
|
|
114
|
+
return 'publish:push';
|
|
115
|
+
return 'publish';
|
|
116
|
+
}
|
|
117
|
+
return TELEMETRY_COMMANDS.includes(command) && command !== 'unknown' && command !== 'none'
|
|
118
|
+
? command
|
|
119
|
+
: 'unknown';
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Fire one event for a finished command, if and only if the user opted in. Returns whether a
|
|
123
|
+
* send was attempted (for tests). Never throws; never delays the caller: the POST happens in a
|
|
124
|
+
* detached child that outlives this process.
|
|
125
|
+
*/
|
|
126
|
+
export function recordCliEvent(opts) {
|
|
127
|
+
try {
|
|
128
|
+
// Agent/automation modes carry no telemetry at all, even for opted-in installs.
|
|
129
|
+
if (opts.argv.includes('--json') || opts.argv.includes('--yes') || opts.argv.includes('-y'))
|
|
130
|
+
return false;
|
|
131
|
+
if (!telemetryEnabled(opts.env, opts.ioEnv))
|
|
132
|
+
return false;
|
|
133
|
+
const payload = {
|
|
134
|
+
installId: loadTelemetryDecision(opts.env)?.installId || 'unset',
|
|
135
|
+
command: commandFromArgv(opts.argv),
|
|
136
|
+
exitCode: Number.isInteger(opts.exitCode) ? opts.exitCode : 1,
|
|
137
|
+
version: opts.version,
|
|
138
|
+
platform: process.platform,
|
|
139
|
+
};
|
|
140
|
+
const origin = (opts.ioEnv.SELLA_MCP_URL || `${DEFAULT_ORIGIN}/api/mcp`).replace(/\/api\/mcp\/?$/, '') || DEFAULT_ORIGIN;
|
|
141
|
+
const sender = opts.senderPath || fileURLToPath(new URL('./telemetry-send.js', import.meta.url));
|
|
142
|
+
const spawnFn = opts.spawnFn || spawn;
|
|
143
|
+
const child = spawnFn(process.execPath, [sender, `${origin}/api/telemetry/cli`, JSON.stringify(payload)], {
|
|
144
|
+
detached: true,
|
|
145
|
+
stdio: 'ignore',
|
|
146
|
+
});
|
|
147
|
+
child.unref();
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sella-cli",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.6.6",
|
|
4
|
+
"description": "Connect your AI agent to Sella, the marketplace where agents buy data and APIs, in one command: npx sella-cli. Installs the Sella MCP server into Claude Code, Cursor and more, then pairs, verifies, funds, and publishes.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"mcp",
|
|
8
|
+
"model-context-protocol",
|
|
9
|
+
"ai-agents",
|
|
10
|
+
"agentic-commerce",
|
|
11
|
+
"x402",
|
|
12
|
+
"usdc",
|
|
13
|
+
"micropayments",
|
|
14
|
+
"pay-per-call",
|
|
15
|
+
"data-marketplace",
|
|
16
|
+
"api-marketplace",
|
|
17
|
+
"datasets",
|
|
18
|
+
"claude",
|
|
19
|
+
"cursor",
|
|
20
|
+
"developer-tools",
|
|
21
|
+
"onboarding"
|
|
22
|
+
],
|
|
5
23
|
"license": "MIT",
|
|
6
24
|
"homepage": "https://sellag.vercel.app",
|
|
25
|
+
"bugs": { "url": "https://github.com/010100100100011101010100/ogsella/issues" },
|
|
7
26
|
"repository": { "type": "git", "url": "https://github.com/010100100100011101010100/ogsella", "directory": "cli" },
|
|
8
27
|
"type": "module",
|
|
9
28
|
"bin": { "sella": "./dist/index.js" },
|