sella-cli 0.6.0 → 0.6.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # sella-cli
2
2
 
3
- Terminal onboarding for [Sella](https://sella.network) — the marketplace where agents buy datasets,
3
+ Terminal onboarding for [Sella](https://sellag.vercel.app) — the marketplace where agents buy datasets,
4
4
  models, and machine-payable APIs.
5
5
 
6
6
  ```bash
package/dist/fund.js CHANGED
@@ -2,9 +2,9 @@ import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import { fetchChainRegistry, chainCapability } from './chains.js';
4
4
  function platformOrigin(mcpUrl) {
5
- return mcpUrl.replace(/\/api\/mcp\/?$/, '') || 'https://sella.network';
5
+ return mcpUrl.replace(/\/api\/mcp\/?$/, '') || 'https://sellag.vercel.app';
6
6
  }
7
- export function getFundingInfo(env, mcpUrl = 'https://sella.network/api/mcp') {
7
+ export function getFundingInfo(env, mcpUrl = 'https://sellag.vercel.app/api/mcp') {
8
8
  const fundingUrl = `${platformOrigin(mcpUrl)}/dashboard/funding`;
9
9
  const configPath = path.join(env.home, '.sella-agentwallet', 'config.json');
10
10
  let config = null;
package/dist/index.js CHANGED
@@ -11,8 +11,8 @@ 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
- const DEFAULT_MCP_URL = 'https://sella.network/api/mcp';
15
- const VERSION = '0.6.0';
14
+ const DEFAULT_MCP_URL = 'https://sellag.vercel.app/api/mcp';
15
+ const VERSION = '0.6.2';
16
16
  function parseFlags(argv) {
17
17
  const flags = {
18
18
  json: false, yes: false, noColor: false, dryRun: false, noKeychain: false,
@@ -162,6 +162,9 @@ async function runPair(ctx, flags, emit, extra = {}) {
162
162
  keyStorage: outcome.save.keyStorage,
163
163
  files: outcome.save.files,
164
164
  agentWallet: outcome.payload.agentWallet?.status || 'none',
165
+ ...(outcome.payload.agentWallet?.status === 'unavailable' && outcome.payload.agentWallet?.reason
166
+ ? { agentWalletReason: outcome.payload.agentWallet.reason }
167
+ : {}),
165
168
  },
166
169
  };
167
170
  }
@@ -206,7 +209,8 @@ function wizardAsk(ui, ctx) {
206
209
  async function cmdInit(ctx, flags, printer) {
207
210
  const ui = new Ui(ctx.io, { json: flags.json, noColor: flags.noColor, yes: flags.yes });
208
211
  const emit = ui.pretty ? uiEmitter(ui, printer) : printer;
209
- const origin = ctx.mcpUrl.replace(/\/api\/mcp\/?$/, '') || 'https://sella.network';
212
+ const origin = ctx.mcpUrl.replace(/\/api\/mcp\/?$/, '') || 'https://sellag.vercel.app';
213
+ const startedAt = Date.now();
210
214
  let host = ctx.mcpUrl;
211
215
  try {
212
216
  host = new URL(ctx.mcpUrl).host;
@@ -214,24 +218,27 @@ async function cmdInit(ctx, flags, printer) {
214
218
  catch {
215
219
  /* keep full URL */
216
220
  }
217
- ui.intro('Sella connect your agents', `v${VERSION} · ${host}`);
221
+ ui.banner('Connect your agents to the marketplace', `v${VERSION} · ${host}`);
218
222
  // A taste of the marketplace (free sandbox, best-effort — never blocks onboarding).
219
223
  const taste = ui.spinner('Peeking at the marketplace (free, no signup)…');
220
224
  try {
221
225
  const result = await sandboxSearch(ctx.mcpUrl, 'data');
222
226
  const picks = [
223
- ...result.datasets.slice(0, 2).map((d) => `dataset ${d.title || d.id}`),
224
- ...result.apis.slice(0, 2).map((a) => `api ${a.name || ''}`),
227
+ ...result.datasets.slice(0, 2).map((d) => ({
228
+ tag: 'dataset',
229
+ text: `${d.title || d.id}${d.priceUSDC ? ` ${String(d.priceUSDC)} USDC` : ''}`,
230
+ tone: 'brand',
231
+ })),
232
+ ...result.apis.slice(0, 2).map((a) => ({ tag: 'api', text: a.name || '', tone: 'ok' })),
225
233
  ];
226
234
  taste.succeed(picks.length ? 'A taste of what your agent can buy here:' : 'Marketplace preview is empty right now.');
227
- for (const p of picks)
228
- ui.detail(p);
235
+ ui.rows(picks);
229
236
  }
230
237
  catch {
231
238
  taste.succeed('Marketplace preview skipped.');
232
239
  }
233
240
  ui.bar();
234
- // Step 1/3 — install
241
+ // Phase 1 — install
235
242
  const detected = detectClients(ctx.env);
236
243
  const targets = selectClientIds(flags, detected);
237
244
  if (targets.length === 0) {
@@ -240,30 +247,31 @@ async function cmdInit(ctx, flags, printer) {
240
247
  ui.outro('Nothing to do.');
241
248
  return 2;
242
249
  }
243
- ui.step(`Step 1/3 · Install the Sella MCP server (${targets.join(', ')})`);
250
+ ui.phase(1, 3, 'INSTALL', `Sella MCP server ${targets.join(', ')}`);
244
251
  const install = installAll(ctx, flags, emit);
245
252
  ui.bar();
246
- // Step 2/3 — pair
247
- ui.step('Step 2/3 · Pair this machine with your Sella account');
253
+ // Phase 2 — pair
254
+ ui.phase(2, 3, 'PAIR', 'Link this machine to your Sella account');
248
255
  const choice = await collectPairChoice(ui, flags, ctx);
249
256
  if (choice.email)
250
257
  ui.step(`Requesting a verification code for ${choice.email}…`);
251
258
  const paired = await runPair(ctx, flags, emit, { ...choice, ask: wizardAsk(ui, ctx) });
252
259
  let reinstalled = [];
253
260
  if (paired.code === 0 && paired.apiKey && !flags.dryRun) {
254
- ui.bar();
255
261
  ui.step('Wiring your API key into the client configs');
256
262
  reinstalled = installAll(ctx, flags, emit, paired.apiKey).results;
257
263
  }
258
- // Step 3/3 — verify (report-only: exit codes stay install/pair-driven so automation is stable)
264
+ // Phase 3 — verify (report-only: exit codes stay install/pair-driven so automation is stable)
259
265
  let verifySummary;
260
266
  if (paired.code === 0 && !flags.dryRun) {
261
267
  ui.bar();
262
- const sp = ui.spinner('Step 3/3 · Verifying the install (sella doctor)');
268
+ ui.phase(3, 3, 'VERIFY', 'Prove the install works (sella doctor)');
269
+ const sp = ui.spinner('Checking endpoint, credentials, auth, wallets, pay-quote…');
263
270
  try {
264
271
  const verify = await runDoctor({ mcpUrl: ctx.mcpUrl, env: ctx.env });
265
272
  const passed = verify.checks.filter((c) => c.ok).length;
266
- sp.succeed(`Step 3/3 · Verified — ${passed}/${verify.checks.length} doctor checks passed`);
273
+ (verify.ok ? sp.succeed : sp.succeed)(`${passed}/${verify.checks.length} checks passed`);
274
+ ui.checkStrip(verify.checks.map((c) => ({ id: c.id, ok: c.ok })));
267
275
  for (const check of verify.checks.filter((c) => !c.ok)) {
268
276
  ui.warn(`${check.label} — ${check.detail}`);
269
277
  if (check.fix)
@@ -272,17 +280,23 @@ async function cmdInit(ctx, flags, printer) {
272
280
  verifySummary = { ok: verify.ok, passed, total: verify.checks.length };
273
281
  }
274
282
  catch (err) {
275
- sp.fail('Step 3/3 · Verify could not run (network?) — try `sella doctor` later.');
283
+ sp.fail('Verify could not run (network?) — try `sella doctor` later.');
276
284
  verifySummary = { ok: false, error: err instanceof Error ? err.message : String(err) };
277
285
  }
278
286
  }
279
287
  if (paired.code === 0) {
280
- ui.note('Sella is connected', [
288
+ const awStatus = String(paired.summary?.agentWallet || 'none');
289
+ const lines = [
281
290
  `Fund your agent wallet: ${origin}/dashboard/funding`,
282
291
  'Try it free: sella sandbox "web search"',
283
292
  'Health check: sella doctor',
284
- ]);
285
- ui.outro(flags.dryRun ? 'Dry run complete — nothing was written.' : 'Done — your agents can now buy on Sella.');
293
+ ];
294
+ if (awStatus === 'unavailable') {
295
+ lines.push('AgentWallet pending (backend not configured) — re-run `sella pair` later.');
296
+ }
297
+ ui.note('Sella is connected', lines, flags.dryRun ? 'plain' : awStatus === 'unavailable' ? 'warn' : 'ok');
298
+ const took = Math.max(1, Math.round((Date.now() - startedAt) / 1000));
299
+ ui.outro(flags.dryRun ? 'Dry run complete — nothing was written.' : `Done in ${took}s — your agents can now buy on Sella.`);
286
300
  }
287
301
  else {
288
302
  ui.outro('Pairing incomplete — fix the issue above, then re-run `sella init`.');
@@ -348,7 +362,7 @@ export async function runCli(argv, io = defaultIo(), env = defaultEnv()) {
348
362
  catch {
349
363
  /* keep full URL */
350
364
  }
351
- ui.intro('Sella — pair this machine', `v${VERSION} · ${host}`);
365
+ ui.banner('Pair this machine', `v${VERSION} · ${host}`);
352
366
  const choice = await collectPairChoice(ui, flags, ctx);
353
367
  if (choice.email)
354
368
  ui.step(`Requesting a verification code for ${choice.email}…`);
@@ -442,7 +456,7 @@ export async function runCli(argv, io = defaultIo(), env = defaultEnv()) {
442
456
  }
443
457
  case 'publish': {
444
458
  const sub = flags.positional[1];
445
- const origin = ctx.mcpUrl.replace(/\/api\/mcp\/?$/, '') || 'https://sella.network';
459
+ const origin = ctx.mcpUrl.replace(/\/api\/mcp\/?$/, '') || 'https://sellag.vercel.app';
446
460
  if (sub === 'init') {
447
461
  const csv = flags.positional.slice(2).find((s) => !s.startsWith('--'));
448
462
  if (!csv) {
package/dist/pairing.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { claimSetupCode, completeEmailOtp, startEmailOtp } from './api.js';
2
2
  import { saveCredentials } from './credentials.js';
3
3
  const NO_TTY_HELP = 'Non-interactive run without credentials. Pass --setup-code <SELLA-…>, set SELLA_SETUP_CODE, ' +
4
- 'or run interactively. Mint a code at https://sella.network/onboarding (Connect your agent).';
4
+ 'or run interactively. Mint a code at https://sellag.vercel.app/onboarding (Connect your agent).';
5
5
  export async function pair(opts) {
6
6
  const finish = (payload, method) => {
7
7
  if (!payload.ok || !payload.apiKey) {
@@ -24,7 +24,7 @@ function main() {
24
24
  '',
25
25
  ` ${bold(cmd)}`,
26
26
  '',
27
- ` ${dim('Docs: https://sella.network · Try without an account: `' + cmd + ' sandbox "web search"`')}`,
27
+ ` ${dim('Docs: https://sellag.vercel.app · Try without an account: `' + cmd + ' sandbox "web search"`')}`,
28
28
  '',
29
29
  ].join('\n'));
30
30
  }
package/dist/ui.js CHANGED
@@ -54,6 +54,68 @@ export class Ui {
54
54
  this.out(`${this.dim('┌')} ${this.paint('1', title)}${meta ? ` ${this.dim(meta)}` : ''}`);
55
55
  this.bar();
56
56
  }
57
+ /** Branded header panel — the first thing the wizard paints. */
58
+ banner(subtitle, meta) {
59
+ if (this.quiet)
60
+ return;
61
+ if (!this.pretty) {
62
+ this.out(`SELLA — ${subtitle} (${meta})`);
63
+ return;
64
+ }
65
+ const wordmark = 'S E L L A';
66
+ const inner = Math.min(this.width() - 2, Math.max(wordmark.length, subtitle.length, meta.length) + 6);
67
+ const pad = (raw, painted = raw) => `${this.accent('│')} ${painted}${' '.repeat(Math.max(0, inner - raw.length - 2))}${this.accent('│')}`;
68
+ this.out('');
69
+ this.out(this.accent('╭' + '─'.repeat(inner) + '╮'));
70
+ this.out(pad(wordmark, this.paint('1;36', wordmark)));
71
+ this.out(pad(subtitle, this.paint('1', subtitle)));
72
+ this.out(pad(meta, this.dim(meta)));
73
+ this.out(this.accent('╰' + '─'.repeat(inner) + '╯'));
74
+ this.out('');
75
+ }
76
+ /** Inverse-video chip, e.g. ` 1/3 INSTALL `. */
77
+ chipText(label, tone = 'brand') {
78
+ if (!this.color)
79
+ return `[${label}]`;
80
+ const bg = tone === 'ok' ? '42;30' : tone === 'warn' ? '43;30' : '46;30';
81
+ return `\x1b[${bg}m ${label} \x1b[0m`;
82
+ }
83
+ /** Phase header: chip + progress dots + bold title. */
84
+ phase(n, total, label, title) {
85
+ if (this.quiet)
86
+ return;
87
+ if (!this.pretty) {
88
+ this.out(`[${n}/${total} ${label}] ${title}`);
89
+ return;
90
+ }
91
+ const dots = Array.from({ length: total }, (_, i) => i < n ? this.accent('●') : this.dim('○')).join(' ');
92
+ this.out(`${this.chipText(`${n}/${total} ${label}`)} ${this.paint('1', title)} ${dots}`);
93
+ }
94
+ /** Aligned two-column rows with a colored left tag, for lists like the marketplace taste. */
95
+ rows(items) {
96
+ if (this.quiet || items.length === 0)
97
+ return;
98
+ const w = Math.max(...items.map((i) => i.tag.length));
99
+ for (const item of items) {
100
+ const padded = item.tag.padEnd(w);
101
+ const tag = item.tone === 'ok' ? this.paint('32', padded) : item.tone === 'dim' ? this.dim(padded) : this.paint('35', padded);
102
+ if (!this.pretty)
103
+ this.out(` ${padded} ${item.text}`);
104
+ else
105
+ this.out(`${this.dim('│')} ${tag} ${item.text}`);
106
+ }
107
+ }
108
+ /** One-line ✓/✕ strip summarizing named checks, e.g. the doctor verify. */
109
+ checkStrip(checks) {
110
+ if (this.quiet || checks.length === 0)
111
+ return;
112
+ const parts = checks.map((c) => c.ok ? `${this.paint('32', '✓')} ${this.dim(c.id)}` : `${this.paint('31', '✕')} ${this.paint('31', c.id)}`);
113
+ if (!this.pretty) {
114
+ this.out(' ' + checks.map((c) => `${c.ok ? 'ok' : 'FAIL'}:${c.id}`).join(' '));
115
+ return;
116
+ }
117
+ this.out(`${this.dim('│')} ${parts.join(' ')}`);
118
+ }
57
119
  outro(text) {
58
120
  if (this.quiet)
59
121
  return;
@@ -94,8 +156,8 @@ export class Ui {
94
156
  return;
95
157
  this.out(this.pretty ? `${this.dim('│')} ${text}` : ` ${text}`);
96
158
  }
97
- /** Boxed panel for the final summary / next steps. */
98
- note(title, lines) {
159
+ /** Boxed panel for the final summary / next steps. Tone colors the border. */
160
+ note(title, lines, tone = 'plain') {
99
161
  if (this.quiet)
100
162
  return;
101
163
  if (!this.pretty) {
@@ -105,6 +167,7 @@ export class Ui {
105
167
  this.out(` ${line}`);
106
168
  return;
107
169
  }
170
+ const edge = (s) => tone === 'ok' ? this.paint('32', s) : tone === 'warn' ? this.paint('33', s) : this.dim(s);
108
171
  const inner = this.width() - 6;
109
172
  const wrapped = [];
110
173
  for (const line of lines) {
@@ -124,13 +187,13 @@ export class Ui {
124
187
  }
125
188
  const w = Math.min(this.width() - 2, Math.max(title.length, ...wrapped.map((l) => l.length), 20) + 4);
126
189
  this.bar();
127
- this.out(`${this.dim('╭' + '─'.repeat(w) + '╮')}`);
190
+ this.out(`${edge('╭' + '─'.repeat(w) + '╮')}`);
128
191
  // Pad from the raw string length, then paint — ANSI codes must not count toward width.
129
- const pad = (s, painted = s) => `${this.dim('│')} ${painted}${' '.repeat(Math.max(0, w - s.length - 2))}${this.dim('│')}`;
192
+ const pad = (s, painted = s) => `${edge('│')} ${painted}${' '.repeat(Math.max(0, w - s.length - 2))}${edge('│')}`;
130
193
  this.out(pad(title, this.paint('1', title)));
131
194
  for (const line of wrapped)
132
195
  this.out(pad(line));
133
- this.out(`${this.dim('╰' + '─'.repeat(w) + '╯')}`);
196
+ this.out(`${edge('╰' + '─'.repeat(w) + '╯')}`);
134
197
  }
135
198
  // ── spinner ────────────────────────────────────────────────────────────────
136
199
  spinner(label) {
@@ -148,10 +211,13 @@ export class Ui {
148
211
  ensureCursorRestoredOnExit();
149
212
  let current = label;
150
213
  let i = 0;
214
+ const startedAt = Date.now();
151
215
  process.stdout.write('\x1b[?25l');
152
216
  const render = () => {
153
217
  const frame = this.accent(FRAMES[i % FRAMES.length]);
154
- process.stdout.write(`\r\x1b[K${frame} ${current}`);
218
+ const elapsed = Date.now() - startedAt;
219
+ const suffix = elapsed > 2_000 ? ` ${this.dim(`(${Math.round(elapsed / 1000)}s)`)}` : '';
220
+ process.stdout.write(`\r\x1b[K${frame} ${current}${suffix}`);
155
221
  i += 1;
156
222
  };
157
223
  render();
@@ -160,7 +226,9 @@ export class Ui {
160
226
  clearInterval(timer);
161
227
  process.stdout.write(`\r\x1b[K`);
162
228
  process.stdout.write('\x1b[?25h');
163
- this.out(`${this.paint(glyphCode, glyph)} ${text || current}`);
229
+ const elapsed = Date.now() - startedAt;
230
+ const suffix = elapsed > 2_000 ? ` ${this.dim(`(${(elapsed / 1000).toFixed(1)}s)`)}` : '';
231
+ this.out(`${this.paint(glyphCode, glyph)} ${text || current}${suffix}`);
164
232
  };
165
233
  return {
166
234
  update: (text) => {
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "sella-cli",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Sella onboarding CLI — install Sella into your agent clients, pair, verify, fund, publish. (`npx sella-cli init`)",
5
5
  "license": "MIT",
6
- "homepage": "https://sella.network",
6
+ "homepage": "https://sellag.vercel.app",
7
7
  "repository": { "type": "git", "url": "https://github.com/010100100100011101010100/ogsella", "directory": "cli" },
8
8
  "type": "module",
9
9
  "bin": { "sella": "./dist/index.js" },