@inversealtruism/cairn-cli 0.1.0 → 0.2.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/README.md CHANGED
@@ -41,6 +41,11 @@ cairn watch # live auto-refreshing board
41
41
  cairn recent # recent proposals and support
42
42
  cairn show <id> # item detail and integrity check
43
43
  cairn verify <id> # recompute the content hash and check it
44
+ cairn wall # the Wall — top stones + the reigning King
45
+ cairn network # live network telemetry (alias: cairn stats)
46
+ cairn quests # open quests
47
+ cairn profile <addr> # identity + on-chain reputation
48
+ cairn leaderboard # top builders by reputation
44
49
  cairn ls --json # machine-readable output
45
50
  ```
46
51
 
package/dist/cli.js CHANGED
@@ -205,6 +205,100 @@ async function cmdDomains() {
205
205
  console.log(` ${c.cyan(pad(d.key, 20))} ${c.gray((d.count != null ? d.count + " items" : "") + (d.totalWeight ? " · " + csdToCoins(d.totalWeight) + " CSD" : ""))}`);
206
206
  }
207
207
  }
208
+ async function cmdWall() {
209
+ const r = await api.apiWall();
210
+ const stones = r.stones ?? [];
211
+ banner();
212
+ rule(`the wall · ${r.totals?.stones ?? 0} stones · ${r.totals?.boosts ?? 0} boosts · epoch ${r.epoch ?? "?"}`);
213
+ if (r.king)
214
+ console.log(` ${c.green("★ KING")} ${c.white(c.bold(r.king.message))} ${csdFmt(r.king.weight)} ${c.gray("· " + r.king.boosts + " boosts")}`);
215
+ if (!stones.length) {
216
+ console.log(c.gray("\n no stones yet — place one with the Cairn Wallet, or:"));
217
+ console.log(c.green(" cairn propose --domain cairn:wall --title '<message>'"));
218
+ return;
219
+ }
220
+ const max = stones[0]?.weight || 1;
221
+ stones.slice(0, 25).forEach((s, i) => {
222
+ console.log("");
223
+ console.log(` ${c.magenta(c.bold("#" + (i + 1)))} ${c.white(c.bold(s.message))}${i === 0 ? " " + c.green("★") : ""}`);
224
+ console.log(` ${bar(s.weight, max)} ${csdFmt(s.weight)} ${c.gray("·")} ${c.green(String(s.boosts))} ${c.gray("boosts · " + age(s.ts) + " ago")}${(s.tags && s.tags.length) ? c.gray(" #" + s.tags.join(" #")) : ""}`);
225
+ });
226
+ }
227
+ async function cmdNetwork() {
228
+ const [n, s] = await Promise.all([api.apiNetwork(), api.apiStats().catch(() => null)]);
229
+ banner();
230
+ rule("network · compute substrate");
231
+ if (!n || !n.reachable) {
232
+ console.log(err("node unreachable"));
233
+ return;
234
+ }
235
+ const row = (k, v) => console.log(` ${kdim(pad(k, 15))} ${v}`);
236
+ const hr = (g) => (g >= 1000 ? (g / 1000).toFixed(2) + " TH/s" : (g ?? 0).toFixed(1) + " GH/s");
237
+ row("hashrate", `${c.white(hr(n.hashrateGHs))} ${c.gray("(1h " + hr(n.hashrate1h) + " · 24h " + hr(n.hashrate24h) + ")")}`);
238
+ row("block height", `${c.white(String(n.height))} ${c.gray("· last block " + age(n.lastBlockTime) + " ago")}`);
239
+ row("block time", `${c.white((n.avgBlockTimeSecs ?? 0).toFixed(0) + "s")} ${c.gray("(target " + n.targetBlockSecs + "s)")}`);
240
+ row("miners", `${c.white(String(n.minerCount))} ${c.gray("active ~24h")}`);
241
+ row("peers", `${c.white(String(n.peers))} ${c.gray("connected · " + n.knownPeers + " known · mempool " + n.mempoolTxCount)}`);
242
+ row("block reward", `${c.white(n.blockRewardCoins + " CSD")} ${c.gray("· " + Math.round(n.emittedSupplyCoins).toLocaleString() + " CSD emitted")}`);
243
+ row("chain age", c.white((n.chainAgeDays ?? 0).toFixed(1) + " days"));
244
+ row("activity", `${c.green(String(n.proposals))} proposals ${c.gray("·")} ${c.green(String(n.attestations))} attestations ${c.gray("· " + Number(n.transactions).toLocaleString() + " txs")}`);
245
+ if (s)
246
+ row("board", `${c.green(String(s.items))} items ${c.gray("·")} ${c.green(String(s.supports))} supports ${c.gray("·")} ${c.green(String(s.participants))} participants ${c.gray("· " + s.totalSignalCoins + " CSD signal")}`);
247
+ }
248
+ async function cmdProfile(a) {
249
+ const addr = a._[1];
250
+ if (!addr) {
251
+ console.log(warn("usage: ") + c.cyan("cairn profile <addr>"));
252
+ return;
253
+ }
254
+ const r = await api.apiProfile(addr).catch(() => null);
255
+ if (!r || !r.ok) {
256
+ console.log(err("no profile for " + addr));
257
+ return;
258
+ }
259
+ const p = r.profile || {}, rep = r.reputation || {};
260
+ banner();
261
+ rule(`profile · ${p.handle || addr}`);
262
+ if (p.handle)
263
+ console.log(` ${kdim(pad("handle", 13))} ${c.white(p.handle)}`);
264
+ if (p.bio)
265
+ console.log(` ${kdim(pad("bio", 13))} ${c.gray(p.bio)}`);
266
+ if (p.github)
267
+ console.log(` ${kdim(pad("github", 13))} ${c.cyan(p.github)} ${p.githubVerified ? ok("verified") : c.gray("(unverified)")}`);
268
+ console.log(` ${kdim(pad("address", 13))} ${c.gray(p.addr || addr)}`);
269
+ console.log(` ${kdim(pad("trust", 13))} ${c.white((rep.trust ?? 0).toFixed(2))}`);
270
+ console.log(` ${kdim(pad("work", 13))} ${c.green(String(rep.proposed ?? 0))} proposed ${c.gray("·")} ${c.green(String(rep.shipped ?? 0))} shipped ${c.gray("·")} ${c.green(String(rep.acceptedWork ?? 0))} accepted ${c.gray("·")} ${c.green(String(rep.reviews ?? 0))} reviews`);
271
+ }
272
+ async function cmdLeaderboard() {
273
+ const r = await api.apiLeaderboard();
274
+ banner();
275
+ rule("reputation leaderboard");
276
+ const lb = r.leaderboard ?? [];
277
+ if (!lb.length) {
278
+ console.log(c.gray(" no ranked builders yet — reputation accrues from accepted quest work."));
279
+ return;
280
+ }
281
+ lb.slice(0, 25).forEach((e, i) => {
282
+ console.log(` ${c.magenta(c.bold(pad("#" + (i + 1), 4)))} ${c.white(pad(e.handle || e.addr, 26))} ${c.gray("trust")} ${c.white((e.trust ?? 0).toFixed(2))} ${c.gray("· " + (e.shipped ?? e.acceptedWork ?? 0) + " shipped · " + (e.proposed ?? 0) + " proposed")}`);
283
+ });
284
+ }
285
+ async function cmdQuests() {
286
+ const r = await api.apiQuests();
287
+ banner();
288
+ rule("quests");
289
+ const qs = r.quests ?? [];
290
+ if (!qs.length) {
291
+ console.log(c.gray(" no open quests yet."));
292
+ return;
293
+ }
294
+ qs.slice(0, 25).forEach((q, i) => {
295
+ const reward = q.quest?.reward?.build ? csdToCoins(q.quest.reward.build) + " CSD" : "—";
296
+ console.log("");
297
+ console.log(` ${c.magenta(c.bold("#" + (i + 1)))} ${c.white(c.bold(q.title))} ${c.gray("· " + (q.status || "?"))}`);
298
+ console.log(` ${c.gray("reward " + reward + " · demand " + csdFmt(q.demandWeight || 0) + " · " + (q.demandSupporters || 0) + " backers")}`);
299
+ console.log(c.gray(` id ${String(q.id).slice(0, 22)}…`));
300
+ });
301
+ }
208
302
  async function help() {
209
303
  await bannerAnimated();
210
304
  const cmd = (n, args, d) => console.log(` ${c.cyan(pad(n, 9))} ${c.gray(pad(args, 44))} ${c.dim(d)}`);
@@ -216,6 +310,11 @@ async function help() {
216
310
  cmd("recent", "", "recent proposals + support");
217
311
  cmd("show", "<id>", "item detail + integrity");
218
312
  cmd("verify", "<id>", "recompute hash, check vs chain");
313
+ cmd("wall", "", "the Wall — top stones + King");
314
+ cmd("network", "", "live network telemetry (alias: stats)");
315
+ cmd("quests", "", "open quests");
316
+ cmd("profile", "<addr>", "identity + reputation");
317
+ cmd("leaderboard", "", "top builders by reputation");
219
318
  cmd("propose", "--domain <d> --title <t> --body <b>", "post an item (needs CAIRN_TOKEN)");
220
319
  cmd("support", "<id> --fee <base>", "back an item (needs CAIRN_TOKEN)");
221
320
  console.log(c.gray("\n lenses (--sort): " + Object.keys(LENS).join(" · ")));
@@ -235,6 +334,13 @@ async function main() {
235
334
  case "recent": return cmdRecent();
236
335
  case "show": return cmdShow(a);
237
336
  case "verify": return cmdVerify(a);
337
+ case "wall": return cmdWall();
338
+ case "network":
339
+ case "stats": return cmdNetwork();
340
+ case "quests": return cmdQuests();
341
+ case "profile": return cmdProfile(a);
342
+ case "leaderboard":
343
+ case "lb": return cmdLeaderboard();
238
344
  case "propose": return cmdPropose(a);
239
345
  case "support": return cmdSupport(a);
240
346
  default: return help();
package/dist/lib/api.js CHANGED
@@ -29,6 +29,11 @@ export const apiStats = () => req("/api/stats");
29
29
  export const apiBoard = (domain, window) => req(`/api/board?domain=${encodeURIComponent(domain)}&window=${encodeURIComponent(window)}`);
30
30
  export const apiItem = (id) => req(`/api/item/${encodeURIComponent(id)}`);
31
31
  export const apiActivity = () => req("/api/activity");
32
+ export const apiWall = () => req("/api/wall");
33
+ export const apiNetwork = () => req("/api/network");
34
+ export const apiProfile = (addr) => req(`/api/profile/${encodeURIComponent(addr)}`);
35
+ export const apiLeaderboard = () => req("/api/leaderboard");
36
+ export const apiQuests = () => req("/api/quests");
32
37
  export const apiPropose = (body) => writeReq("/api/propose", body);
33
38
  export const apiSupport = (body) => writeReq("/api/support", body);
34
39
  // optional: query a raw csd node RPC (for trustless verify)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inversealtruism/cairn-cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Command-line client for a Cairn signal board on Compute Substrate — browse, propose, and back what to build.",
5
5
  "type": "module",
6
6
  "bin": { "cairn": "dist/cli.js" },