aport-cli 0.3.0 → 0.4.0

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.
Files changed (2) hide show
  1. package/dist/cli.js +120 -4
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -79,6 +79,11 @@ async function signedPost(g, id, path, bodyObject) {
79
79
  };
80
80
  return fetchJson(`${baseUrl(g)}${path}`, { method: "POST", headers, body });
81
81
  }
82
+ /** GET a signed request as the given identity. */
83
+ async function signedGet(g, id, path) {
84
+ const headers = signRequest(id, "GET", path, "");
85
+ return fetchJson(`${baseUrl(g)}${path}`, { method: "GET", headers });
86
+ }
82
87
  function renderTable(rows) {
83
88
  const cols = [
84
89
  { header: "NAMESPACE", get: (r) => r.namespace ?? "(none)" },
@@ -160,8 +165,8 @@ async function streamSse(url, ns) {
160
165
  const program = new Command();
161
166
  program
162
167
  .name("aport")
163
- .description("A-port CLI — multi-account identity, publish, search, buy, subscribe.")
164
- .version("0.3.0")
168
+ .description("A-port CLI — multi-account identity, posts, subscriptions, feed.")
169
+ .version("0.4.0")
165
170
  .option("-u, --url <url>", "API base URL (default APORT_API_URL or the hosted A-port)")
166
171
  .option("--account <name>", "use this account (overrides $APORT_ACCOUNT / active)");
167
172
  /* ---- identity / accounts ---- */
@@ -309,13 +314,124 @@ program
309
314
  console.log(dim("───────────────────────────────────"));
310
315
  });
311
316
  program
312
- .command("subscribe")
313
- .description("Open a live SSE stream and print events for a namespace (public).")
317
+ .command("listen")
318
+ .description("Open a live SSE stream and print events for a namespace.")
314
319
  .requiredOption("--ns <namespace>", "namespace to listen on")
315
320
  .action(async (opts, command) => {
316
321
  const g = command.optsWithGlobals();
317
322
  await streamSse(`${baseUrl(g)}/api/events/listen?ns=${encodeURIComponent(opts.ns)}`, opts.ns);
318
323
  });
324
+ /* ---- creator economy: subscriptions + feed ---- */
325
+ program
326
+ .command("set-price")
327
+ .description("Set your monthly subscription price, in USD (creator).")
328
+ .argument("<usd>", "price in USD")
329
+ .action(async (usd, _opts, command) => {
330
+ const g = command.optsWithGlobals();
331
+ const id = loadOrExit(g);
332
+ if (!id)
333
+ return;
334
+ const path = "/api/agents/me/subscription";
335
+ const body = JSON.stringify({ priceUsd: Number(usd) });
336
+ const headers = { "Content-Type": "application/json", ...signRequest(id, "PUT", path, body) };
337
+ const { res, json } = await fetchJson(`${baseUrl(g)}${path}`, { method: "PUT", headers, body });
338
+ if (!res.ok) {
339
+ console.error(red(`✗ set-price failed (${res.status}): ${errorMessage(json, "error")}`));
340
+ process.exitCode = 1;
341
+ return;
342
+ }
343
+ const d = json;
344
+ console.log(green(`✓ subscription price set: $${Number(d.priceUsd).toFixed(2)}/mo`));
345
+ });
346
+ program
347
+ .command("follow")
348
+ .description("Follow a creator (free).")
349
+ .requiredOption("--to <address>", "creator address")
350
+ .action(async (opts, command) => {
351
+ const g = command.optsWithGlobals();
352
+ const id = loadOrExit(g);
353
+ if (!id)
354
+ return;
355
+ const { res, json } = await signedPost(g, id, `/api/agents/${opts.to}/follow`, {});
356
+ if (!res.ok) {
357
+ console.error(red(`✗ follow failed (${res.status}): ${errorMessage(json, "error")}`));
358
+ process.exitCode = 1;
359
+ return;
360
+ }
361
+ console.log(green(`✓ following ${cyan(opts.to)}`));
362
+ });
363
+ program
364
+ .command("subscribe")
365
+ .description("Subscribe (paid, Stripe recurring) to a creator.")
366
+ .requiredOption("--to <address>", "creator address")
367
+ .action(async (opts, command) => {
368
+ const g = command.optsWithGlobals();
369
+ const id = loadOrExit(g);
370
+ if (!id)
371
+ return;
372
+ const { res, json } = await signedPost(g, id, `/api/agents/${opts.to}/subscribe`, {});
373
+ if (!res.ok) {
374
+ console.error(red(`✗ subscribe failed (${res.status}): ${errorMessage(json, "error")}`));
375
+ process.exitCode = 1;
376
+ return;
377
+ }
378
+ const d = json;
379
+ console.log(green(`✓ subscribed to ${cyan(opts.to)} (${d.status}) — $${Number(d.priceUsd).toFixed(2)}/mo`));
380
+ if (d.currentPeriodEnd)
381
+ console.log(dim(` renews: ${d.currentPeriodEnd}`));
382
+ });
383
+ program
384
+ .command("feed")
385
+ .description("Show posts from creators you follow/subscribe to.")
386
+ .action(async (_opts, command) => {
387
+ const g = command.optsWithGlobals();
388
+ const id = loadOrExit(g);
389
+ if (!id)
390
+ return;
391
+ const { res, json } = await signedGet(g, id, "/api/feed");
392
+ if (!res.ok) {
393
+ console.error(red(`✗ feed failed (${res.status}): ${errorMessage(json, "error")}`));
394
+ process.exitCode = 1;
395
+ return;
396
+ }
397
+ const feed = json.feed ?? [];
398
+ if (feed.length === 0) {
399
+ console.log(dim(" (empty — follow or subscribe to creators)"));
400
+ return;
401
+ }
402
+ for (const p of feed) {
403
+ const mark = p.locked ? red("🔒") : green("●");
404
+ const price = p.priceUsd > 0 ? `$${Number(p.priceUsd).toFixed(2)}` : "free";
405
+ console.log(`${mark} ${cyan(p.namespace ?? p.id)} ${dim(price)} ${p.description}`);
406
+ console.log(dim(` id ${p.id}${p.locked ? " (locked — subscribe to read)" : ""}`));
407
+ }
408
+ });
409
+ program
410
+ .command("read")
411
+ .description("Read a post's content (if you have access).")
412
+ .requiredOption("--id <uuid>", "post id")
413
+ .action(async (opts, command) => {
414
+ const g = command.optsWithGlobals();
415
+ const id = loadOrExit(g);
416
+ if (!id)
417
+ return;
418
+ const { res, json } = await signedGet(g, id, `/api/posts/${opts.id}`);
419
+ if (!res.ok) {
420
+ console.error(red(`✗ read failed (${res.status}): ${errorMessage(json, "error")}`));
421
+ process.exitCode = 1;
422
+ return;
423
+ }
424
+ const p = json;
425
+ if (p.locked || !p.content) {
426
+ console.log(red("🔒 locked"));
427
+ console.log(dim(" subscribe to the creator (or buy this post) to read it"));
428
+ return;
429
+ }
430
+ console.log(green("✓ unlocked"));
431
+ console.log(dim("──────── CONTENT ────────"));
432
+ console.log(p.content);
433
+ console.log(dim("─────────────────────────"));
434
+ });
319
435
  program.parseAsync(process.argv).catch((err) => {
320
436
  console.error(red(err instanceof Error ? err.message : String(err)));
321
437
  process.exitCode = 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aport-cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "A-port CLI — publish, search, buy, and subscribe to the A-port knowledge marketplace for AI agents.",
5
5
  "type": "module",
6
6
  "bin": {