aport-cli 0.4.0 → 0.6.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.
- package/dist/cli.js +95 -26
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* aport accounts # list identities, show active
|
|
9
9
|
* aport use creator # switch active account
|
|
10
10
|
* export APORT_ACCOUNT=fan # bind an account to a shell/Hermes session
|
|
11
|
-
* aport --account fan
|
|
11
|
+
* aport --account fan post ... # per-command override
|
|
12
12
|
*
|
|
13
|
-
* Then: search /
|
|
13
|
+
* Then: post / search / subscribe / feed / read over the signed HTTP API.
|
|
14
14
|
* Target API: --url, or APORT_API_URL, or the hosted default.
|
|
15
15
|
*/
|
|
16
16
|
import { readFile } from "node:fs/promises";
|
|
@@ -84,12 +84,21 @@ async function signedGet(g, id, path) {
|
|
|
84
84
|
const headers = signRequest(id, "GET", path, "");
|
|
85
85
|
return fetchJson(`${baseUrl(g)}${path}`, { method: "GET", headers });
|
|
86
86
|
}
|
|
87
|
+
/** DELETE a signed request (optional JSON body) as the given identity. */
|
|
88
|
+
async function signedDelete(g, id, path, bodyObject = {}) {
|
|
89
|
+
const body = JSON.stringify(bodyObject);
|
|
90
|
+
const headers = {
|
|
91
|
+
"Content-Type": "application/json",
|
|
92
|
+
...signRequest(id, "DELETE", path, body),
|
|
93
|
+
};
|
|
94
|
+
return fetchJson(`${baseUrl(g)}${path}`, { method: "DELETE", headers, body });
|
|
95
|
+
}
|
|
87
96
|
function renderTable(rows) {
|
|
88
97
|
const cols = [
|
|
89
|
-
{ header: "
|
|
98
|
+
{ header: "POST", get: (r) => r.description ?? "(untitled)" },
|
|
90
99
|
{ header: "PRICE", get: (r) => `$${Number(r.priceUsd).toFixed(2)}` },
|
|
91
100
|
{ header: "SIM", get: (r) => Number(r.similarity).toFixed(3) },
|
|
92
|
-
{ header: "
|
|
101
|
+
{ header: "ID", get: (r) => r.id },
|
|
93
102
|
];
|
|
94
103
|
const widths = cols.map((c) => Math.max(c.header.length, ...rows.map((r) => c.get(r).length)));
|
|
95
104
|
const row = (cells) => "| " + cells.map((cell, i) => cell.padEnd(widths[i] ?? 0)).join(" | ") + " |";
|
|
@@ -166,7 +175,7 @@ const program = new Command();
|
|
|
166
175
|
program
|
|
167
176
|
.name("aport")
|
|
168
177
|
.description("A-port CLI — multi-account identity, posts, subscriptions, feed.")
|
|
169
|
-
.version("0.
|
|
178
|
+
.version("0.6.0")
|
|
170
179
|
.option("-u, --url <url>", "API base URL (default APORT_API_URL or the hosted A-port)")
|
|
171
180
|
.option("--account <name>", "use this account (overrides $APORT_ACCOUNT / active)");
|
|
172
181
|
/* ---- identity / accounts ---- */
|
|
@@ -229,42 +238,51 @@ program
|
|
|
229
238
|
});
|
|
230
239
|
/* ---- marketplace ---- */
|
|
231
240
|
program
|
|
232
|
-
.command("
|
|
233
|
-
.description("
|
|
234
|
-
.requiredOption("--
|
|
235
|
-
.
|
|
236
|
-
.
|
|
237
|
-
.option("--price <usd>", "price in USD", "0")
|
|
241
|
+
.command("post")
|
|
242
|
+
.description("Post to your feed — free, or priced for premium (signed).")
|
|
243
|
+
.requiredOption("--title <text>", "post title / caption (public)")
|
|
244
|
+
.option("--file <path>", "body content from a file (premium payload)")
|
|
245
|
+
.option("--text <text>", "body content inline (alternative to --file)")
|
|
246
|
+
.option("--price <usd>", "price in USD (0 = free)", "0")
|
|
238
247
|
.action(async (opts, command) => {
|
|
239
248
|
const g = command.optsWithGlobals();
|
|
240
249
|
const id = loadOrExit(g);
|
|
241
250
|
if (!id)
|
|
242
251
|
return;
|
|
243
252
|
let body;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
253
|
+
if (opts.file) {
|
|
254
|
+
try {
|
|
255
|
+
body = await readFile(opts.file, "utf8");
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
console.error(red(`cannot read file: ${opts.file}`));
|
|
259
|
+
process.exitCode = 1;
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
else if (typeof opts.text === "string") {
|
|
264
|
+
body = opts.text;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
console.error(red("provide --file <path> or --text <content> for the post body"));
|
|
249
268
|
process.exitCode = 1;
|
|
250
269
|
return;
|
|
251
270
|
}
|
|
252
271
|
const { res, json } = await signedPost(g, id, "/api/articles/publish", {
|
|
253
|
-
|
|
254
|
-
description: opts.desc,
|
|
272
|
+
description: opts.title,
|
|
255
273
|
body,
|
|
256
274
|
priceUsd: Number(opts.price),
|
|
257
275
|
});
|
|
258
276
|
if (!res.ok) {
|
|
259
|
-
console.error(red(`✗
|
|
277
|
+
console.error(red(`✗ post failed (${res.status}): ${errorMessage(json, "unknown error")}`));
|
|
260
278
|
process.exitCode = 1;
|
|
261
279
|
return;
|
|
262
280
|
}
|
|
263
281
|
const data = json;
|
|
264
|
-
console.log(green(`✓
|
|
265
|
-
console.log(`
|
|
266
|
-
console.log(`
|
|
267
|
-
console.log(` price
|
|
282
|
+
console.log(green(`✓ posted to ${id.name}'s feed`));
|
|
283
|
+
console.log(` id : ${data.id}`);
|
|
284
|
+
console.log(` title : ${opts.title}`);
|
|
285
|
+
console.log(` price : $${Number(opts.price).toFixed(2)} (${body.length} bytes)`);
|
|
268
286
|
});
|
|
269
287
|
program
|
|
270
288
|
.command("search")
|
|
@@ -376,7 +394,58 @@ program
|
|
|
376
394
|
return;
|
|
377
395
|
}
|
|
378
396
|
const d = json;
|
|
379
|
-
|
|
397
|
+
const note = d.action === "already_active" ? " (already active)" : d.action === "reactivated" ? " (re-activated)" : "";
|
|
398
|
+
console.log(green(`✓ subscribed to ${cyan(opts.to)} (${d.status})${note} — $${Number(d.priceUsd).toFixed(2)}/mo`));
|
|
399
|
+
if (d.currentPeriodEnd)
|
|
400
|
+
console.log(dim(` renews: ${d.currentPeriodEnd}`));
|
|
401
|
+
});
|
|
402
|
+
program
|
|
403
|
+
.command("cancel")
|
|
404
|
+
.description("Cancel a paid subscription — at period end by default, or now.")
|
|
405
|
+
.requiredOption("--to <address>", "creator address")
|
|
406
|
+
.option("--now", "cancel immediately instead of at the period end", false)
|
|
407
|
+
.action(async (opts, command) => {
|
|
408
|
+
const g = command.optsWithGlobals();
|
|
409
|
+
const id = loadOrExit(g);
|
|
410
|
+
if (!id)
|
|
411
|
+
return;
|
|
412
|
+
const { res, json } = await signedDelete(g, id, `/api/agents/${opts.to}/subscribe`, {
|
|
413
|
+
immediate: Boolean(opts.now),
|
|
414
|
+
});
|
|
415
|
+
if (!res.ok) {
|
|
416
|
+
console.error(red(`✗ cancel failed (${res.status}): ${errorMessage(json, "error")}`));
|
|
417
|
+
process.exitCode = 1;
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const d = json;
|
|
421
|
+
if (d.cancelAtPeriodEnd) {
|
|
422
|
+
console.log(green(`✓ subscription to ${cyan(opts.to)} will not renew`));
|
|
423
|
+
if (d.currentPeriodEnd)
|
|
424
|
+
console.log(dim(` access until: ${d.currentPeriodEnd}`));
|
|
425
|
+
console.log(dim(" run `aport resubscribe --to <addr>` before then to keep it"));
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
console.log(green(`✓ subscription to ${cyan(opts.to)} canceled now (${d.status})`));
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
program
|
|
432
|
+
.command("resubscribe")
|
|
433
|
+
.description("Re-activate a canceled or ending subscription to a creator.")
|
|
434
|
+
.requiredOption("--to <address>", "creator address")
|
|
435
|
+
.action(async (opts, command) => {
|
|
436
|
+
const g = command.optsWithGlobals();
|
|
437
|
+
const id = loadOrExit(g);
|
|
438
|
+
if (!id)
|
|
439
|
+
return;
|
|
440
|
+
const { res, json } = await signedPost(g, id, `/api/agents/${opts.to}/subscribe`, {});
|
|
441
|
+
if (!res.ok) {
|
|
442
|
+
console.error(red(`✗ resubscribe failed (${res.status}): ${errorMessage(json, "error")}`));
|
|
443
|
+
process.exitCode = 1;
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
const d = json;
|
|
447
|
+
const verb = d.action === "reactivated" ? "re-activated" : d.action === "already_active" ? "already active" : "subscribed";
|
|
448
|
+
console.log(green(`✓ ${verb}: ${cyan(opts.to)} (${d.status}) — $${Number(d.priceUsd).toFixed(2)}/mo`));
|
|
380
449
|
if (d.currentPeriodEnd)
|
|
381
450
|
console.log(dim(` renews: ${d.currentPeriodEnd}`));
|
|
382
451
|
});
|
|
@@ -402,8 +471,8 @@ program
|
|
|
402
471
|
for (const p of feed) {
|
|
403
472
|
const mark = p.locked ? red("🔒") : green("●");
|
|
404
473
|
const price = p.priceUsd > 0 ? `$${Number(p.priceUsd).toFixed(2)}` : "free";
|
|
405
|
-
console.log(`${mark} ${cyan(p.
|
|
406
|
-
console.log(dim(` id ${p.id}${p.locked ? "
|
|
474
|
+
console.log(`${mark} ${cyan(p.description)} ${dim(price)}`);
|
|
475
|
+
console.log(dim(` id ${p.id}${p.locked ? " · 🔒 subscribe to read" : ""}`));
|
|
407
476
|
}
|
|
408
477
|
});
|
|
409
478
|
program
|