@binaryandthread/cli 0.1.0 → 0.1.1

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
  # @binaryandthread/cli — `bt`
2
2
 
3
- Buy a Binary & Thread heavyweight tee **from your terminal**, with the ceremony of a production
3
+ Buy a Binary & Thread heavyweight piece **from your terminal**, with the ceremony of a production
4
4
  deploy. A small, well-tested client over the Store API — the card never touches the terminal.
5
5
 
6
6
  > `bt` is a dumb client. Every dangerous operation (payment, fulfilment) lives in the backend.
@@ -28,7 +28,7 @@ unambiguous form.
28
28
  | --- | --- |
29
29
  | `bt browse` | the catalog (interactive ↑/↓ picker on a TTY; `--json` for scripts) |
30
30
  | `bt search <q>` | filter the catalog |
31
- | `bt view <handle>` | one tee — specs + mockup (`--colour black` renders the black row) |
31
+ | `bt view <handle>` | one piece — specs + mockup (`--colour black` renders the black row) |
32
32
  | `bt stage <handle>` | add to your cart (`--size`, `--colour`) |
33
33
  | `bt cart` / `bt unstage <ref>` | show / remove a line |
34
34
  | `bt checkout` | mint a checkout, hand off to the browser to pay, then poll |
package/dist/bin.js CHANGED
@@ -15,39 +15,36 @@ import {
15
15
  RecoverableCartError,
16
16
  StoreApiError,
17
17
  StoreClient,
18
+ box,
18
19
  brandOf,
20
+ colourMarker,
21
+ contributionBar,
22
+ cyan,
23
+ dim,
19
24
  displayName,
20
25
  empty,
21
26
  fail,
22
27
  firstPrice,
23
28
  fmtPrice,
24
29
  formatCartLine,
30
+ green,
31
+ header,
25
32
  lineColour,
26
33
  ok,
27
34
  printResult,
28
35
  productColours,
29
36
  redact,
30
- registerBrowse,
31
37
  registerStage,
32
38
  registerView,
33
39
  resolveLineItemRef,
40
+ resolveOutput,
41
+ rule,
34
42
  runStage,
35
43
  stageMarker,
36
44
  versionLine,
37
45
  warn,
38
46
  working
39
- } from "./chunk-DWTH7CNI.js";
40
- import {
41
- box,
42
- colourMarker,
43
- contributionBar,
44
- cyan,
45
- dim,
46
- green,
47
- header,
48
- makeBrand,
49
- rule
50
- } from "./chunk-P3LSUCEO.js";
47
+ } from "./chunk-4TEWFVRJ.js";
51
48
  import {
52
49
  __require
53
50
  } from "./chunk-7OCVIDC7.js";
@@ -75,6 +72,9 @@ function makeLogger(debug, write = (s) => process.stderr.write(s)) {
75
72
  };
76
73
  }
77
74
 
75
+ // src/version.ts
76
+ var VERSION = "0.1.1";
77
+
78
78
  // src/eggs.ts
79
79
  function teapot() {
80
80
  return `${GLYPH.fail} 418 i'm a teapot. we make clothing, not coffee. (RFC 2324)`;
@@ -297,6 +297,84 @@ function deleteToken(deps = {}) {
297
297
  }
298
298
  }
299
299
 
300
+ // src/commands/browse.ts
301
+ function productRow(p, stage, style) {
302
+ const { amount, currency } = firstPrice(p);
303
+ const { title, handle } = displayName(p);
304
+ const colours = colourMarker(productColours(p));
305
+ const parts = [
306
+ title.padEnd(22),
307
+ cyan(style, fmtPrice(amount, currency).padEnd(11)),
308
+ dim(style, handle.padEnd(34)),
309
+ stageMarker(stage)
310
+ ];
311
+ const row = parts.join(" ").trimEnd();
312
+ return colours ? `${row}
313
+ ${dim(style, colours)}` : row;
314
+ }
315
+ async function runBrowse(client, config, args) {
316
+ const style = brandOf(args);
317
+ const region = await client.resolveRegion(config.region);
318
+ const products = await client.listProducts({ region });
319
+ const rows = products.map((p) => ({
320
+ p,
321
+ stage: p.metadata?.["channel_stage"] ?? "main"
322
+ }));
323
+ let drops = [];
324
+ if (args.branch === "next") {
325
+ drops = await client.listDrops();
326
+ }
327
+ const lines = [];
328
+ if (rows.length === 0 && drops.length === 0) {
329
+ lines.push(empty("nothing in stock on this branch yet. main is the latest."));
330
+ lines.push(" peek at next \u2192 bt browse --branch=next");
331
+ } else {
332
+ lines.push(header(style, "bt browse \u2014 in stock"));
333
+ for (const { p, stage } of rows) {
334
+ lines.push(productRow(p, stage, style));
335
+ }
336
+ for (const d of drops) {
337
+ const dt = (d.title ?? d.handle ?? d.id).toString();
338
+ lines.push(
339
+ [
340
+ dt.padEnd(22),
341
+ cyan(style, fmtPrice(d.price, region.currency_code).padEnd(11)),
342
+ dim(style, (d.handle ?? d.id).padEnd(34)),
343
+ stageMarker(d.channel_stage)
344
+ ].join(" ").trimEnd()
345
+ );
346
+ }
347
+ if (drops.length > 0) {
348
+ lines.push(" on next, before the web \u2192 bt drops");
349
+ }
350
+ lines.push(" view one \u2192 bt view <handle> \xB7 stage one \u2192 bt stage <handle> --size L");
351
+ }
352
+ return {
353
+ lines,
354
+ json: args.json ? products.map((p) => ({
355
+ id: p.id,
356
+ handle: p.handle,
357
+ title: p.title,
358
+ price: firstPrice(p).amount,
359
+ currency: firstPrice(p).currency,
360
+ colours: productColours(p),
361
+ channel_stage: p.metadata?.["channel_stage"] ?? "main"
362
+ })) : void 0
363
+ };
364
+ }
365
+ function registerBrowse(program, makeClient2, loadConfig2) {
366
+ program.command("browse").alias("ls").description("list what's in stock on a branch.").option("--branch <branch>", "main (live) or next (terminal-only drops)").option("--plain", "force the static table (skip the interactive picker)").action(async (opts) => {
367
+ const { json, brand } = resolveOutput(program);
368
+ if (!json && !opts.plain && brand.color && process.stdout.isTTY) {
369
+ const { runInteractiveBrowse } = await import("./interactive-HNU7Y3KG.js");
370
+ const handled = await runInteractiveBrowse(makeClient2(), loadConfig2(), { branch: opts.branch, brand });
371
+ if (handled) return;
372
+ }
373
+ const res = await runBrowse(makeClient2(), loadConfig2(), { branch: opts.branch, json, brand });
374
+ printResult(res, json);
375
+ });
376
+ }
377
+
300
378
  // src/commands/search.ts
301
379
  function matches(p, q) {
302
380
  const needle = q.toLowerCase();
@@ -343,9 +421,7 @@ async function runSearch(client, config, args) {
343
421
  }
344
422
  function registerSearch(program, makeClient2, loadConfig2) {
345
423
  program.command("search").argument("<query>", "what to look for").description("filter the catalog by a query.").action(async (query) => {
346
- const gOpts = program.opts();
347
- const json = Boolean(gOpts.json);
348
- const brand = json ? makeBrand({ noColor: true }) : makeBrand({ noColor: gOpts.color === false });
424
+ const { json, brand } = resolveOutput(program);
349
425
  const res = await runSearch(makeClient2(), loadConfig2(), { query, json, brand });
350
426
  printResult(res, json);
351
427
  });
@@ -397,9 +473,7 @@ async function runCart(client, config, args) {
397
473
  }
398
474
  function registerCart(program, makeClient2, loadConfig2) {
399
475
  program.command("cart").description("show your current cart \u2014 the authoritative server state.").action(async () => {
400
- const gOpts = program.opts();
401
- const json = Boolean(gOpts.json);
402
- const brand = json ? makeBrand({ noColor: true }) : makeBrand({ noColor: gOpts.color === false });
476
+ const { json, brand } = resolveOutput(program);
403
477
  const res = await runCart(makeClient2(), loadConfig2(), { json, brand });
404
478
  printResult(res, json);
405
479
  });
@@ -577,9 +651,7 @@ function maskEmail(email) {
577
651
  }
578
652
  function registerCheckout(program, makeClient2, loadConfig2) {
579
653
  const action = async (opts) => {
580
- const gOpts = program.opts();
581
- const json = Boolean(gOpts.json);
582
- const brand = json ? makeBrand({ noColor: true }) : makeBrand({ noColor: gOpts.color === false });
654
+ const { json, brand } = resolveOutput(program);
583
655
  const { makePrompts: makePrompts2 } = await import("./prompts-GTC5VYM6.js");
584
656
  const { openCheckout } = await import("./browser-EO5EQUXQ.js");
585
657
  const { pollUntilPlaced } = await import("./poll-O3VUD5WV.js");
@@ -666,9 +738,7 @@ async function runOrder(client, config, args) {
666
738
  }
667
739
  function registerOrder(program, makeClient2, loadConfig2) {
668
740
  program.command("order").argument("<id>", "the order number (display id)").description("track one order: staged \u2192 committed \u2192 printed \u2192 shipped \u2192 delivered.").action(async (id) => {
669
- const gOpts = program.opts();
670
- const json = Boolean(gOpts.json);
671
- const brand = json ? makeBrand({ noColor: true }) : makeBrand({ noColor: gOpts.color === false });
741
+ const { json, brand } = resolveOutput(program);
672
742
  const res = await runOrder(makeClient2(), loadConfig2(), { displayId: id, json, brand });
673
743
  printResult(res, json);
674
744
  });
@@ -704,7 +774,7 @@ function resolveBridgeToken(env, keychainToken) {
704
774
  }
705
775
  function registerToken(program) {
706
776
  program.command("token").description("print your keychain JWT for an MCP config (env BT_TOKEN). a bearer credential \u2014 handle with care.").action(() => {
707
- const json = Boolean(program.opts().json);
777
+ const { json } = resolveOutput(program);
708
778
  const token = resolveBridgeToken(process.env, getToken());
709
779
  if (!token) {
710
780
  process.stderr.write(empty("not logged in \u2014 run bt login first.") + "\n");
@@ -797,15 +867,14 @@ function renderFlat(orders) {
797
867
  // src/commands/log.ts
798
868
  var EMPTY_STATE2 = "no commits yet. your wardrobe is a fresh repo.";
799
869
  async function runLog(client, config, args) {
800
- const history = client;
801
870
  const loggedIn = args.loggedIn ?? Boolean(config.email);
802
- if (!loggedIn || typeof history.listOrders !== "function") {
871
+ if (!loggedIn) {
803
872
  return {
804
873
  lines: [empty(EMPTY_STATE2), " start one \u2192 bt browse"],
805
874
  json: args.json ? { commits: [] } : void 0
806
875
  };
807
876
  }
808
- const orders = await history.listOrders();
877
+ const orders = await client.listOrders();
809
878
  if (!orders || orders.length === 0) {
810
879
  return {
811
880
  lines: [empty(EMPTY_STATE2), " start one \u2192 bt browse"],
@@ -821,9 +890,7 @@ async function runLog(client, config, args) {
821
890
  }
822
891
  function registerLog(program, makeClient2, loadConfig2) {
823
892
  program.command("log").option("--oneline", "compact one-line-per-order format").option("--graph", "graph format").description("your order history as a git log.").action(async (opts) => {
824
- const gOpts = program.opts();
825
- const json = Boolean(gOpts.json);
826
- const brand = json ? makeBrand({ noColor: true }) : makeBrand({ noColor: gOpts.color === false });
893
+ const { json, brand } = resolveOutput(program);
827
894
  const loggedIn = Boolean(resolveBridgeToken(process.env, getToken()));
828
895
  const res = await runLog(makeClient2(), loadConfig2(), { oneline: opts.oneline, graph: opts.graph, json, loggedIn, brand });
829
896
  printResult(res, json);
@@ -882,9 +949,7 @@ async function runStatus(client, config, args) {
882
949
  }
883
950
  function registerStatus(program, makeClient2, loadConfig2) {
884
951
  program.command("status").description("working-tree state: what's staged, what's in flight.").action(async () => {
885
- const gOpts = program.opts();
886
- const json = Boolean(gOpts.json);
887
- const brand = json ? makeBrand({ noColor: true }) : makeBrand({ noColor: gOpts.color === false });
952
+ const { json, brand } = resolveOutput(program);
888
953
  const res = await runStatus(makeClient2(), loadConfig2(), { json, brand });
889
954
  printResult(res, json);
890
955
  });
@@ -899,15 +964,6 @@ async function runUnstage(client, config, args) {
899
964
  json: args.json ? { cart_id: null, items: [] } : void 0
900
965
  };
901
966
  }
902
- const remover = client;
903
- if (typeof remover.removeLineItem !== "function") {
904
- return {
905
- lines: [
906
- warn("unstage isn't wired to this client build yet."),
907
- " for now: bt forget clears your local cart, or re-stage to overwrite."
908
- ]
909
- };
910
- }
911
967
  try {
912
968
  const before = await client.getCart(config.cart_id);
913
969
  const beforeItems = before.items ?? [];
@@ -927,7 +983,7 @@ async function runUnstage(client, config, args) {
927
983
  json: args.json ? { cart_id: before.id, removed: null, items: beforeItems } : void 0
928
984
  };
929
985
  }
930
- await remover.removeLineItem(config.cart_id, target.id);
986
+ await client.removeLineItem(config.cart_id, target.id);
931
987
  const after = await client.getCart(config.cart_id);
932
988
  const afterItems = after.items ?? [];
933
989
  const stillThere = afterItems.some((li) => li.id === target.id);
@@ -973,7 +1029,7 @@ async function runUnstage(client, config, args) {
973
1029
  }
974
1030
  function registerUnstage(program, makeClient2, loadConfig2) {
975
1031
  program.command("unstage").alias("reset").argument("<ref>", "the staged item: its # (from bt cart), its line-item id, or its name").description("remove a staged item from the cart (git reset).").action(async (ref) => {
976
- const json = Boolean(program.opts().json);
1032
+ const { json } = resolveOutput(program);
977
1033
  const res = await runUnstage(makeClient2(), loadConfig2(), { itemId: ref, json });
978
1034
  printResult(res, json);
979
1035
  });
@@ -1078,9 +1134,7 @@ async function runWhoami(client, config, args, token) {
1078
1134
  }
1079
1135
  function registerWhoami(program, makeClient2, loadConfig2) {
1080
1136
  program.command("whoami").description("your contribution profile + standing.").action(async () => {
1081
- const gOpts = program.opts();
1082
- const json = Boolean(gOpts.json);
1083
- const brand = json ? makeBrand({ noColor: true }) : makeBrand({ noColor: gOpts.color === false });
1137
+ const { json, brand } = resolveOutput(program);
1084
1138
  const res = await runWhoami(makeClient2(), loadConfig2(), { json, brand }, getToken());
1085
1139
  printResult(res, json);
1086
1140
  });
@@ -1132,7 +1186,7 @@ async function runDrops(client, _config, args) {
1132
1186
  }
1133
1187
  function registerDrops(program, makeClient2, loadConfig2) {
1134
1188
  program.command("drops").alias("next").description("what's on next before it hits the web.").action(async () => {
1135
- const json = Boolean(program.opts().json);
1189
+ const { json } = resolveOutput(program);
1136
1190
  const res = await runDrops(makeClient2(), loadConfig2(), { json });
1137
1191
  printResult(res, json);
1138
1192
  });
@@ -1157,7 +1211,7 @@ async function runConnect(args) {
1157
1211
  }
1158
1212
  function registerConnect(program) {
1159
1213
  program.command("connect").description("the contributor on-ramp \u2014 what connecting unlocks. run bt login to connect.").action(async () => {
1160
- const json = Boolean(program.opts().json);
1214
+ const { json } = resolveOutput(program);
1161
1215
  const res = await runConnect({ json });
1162
1216
  printResult(res, json);
1163
1217
  });
@@ -1222,7 +1276,7 @@ async function runLogin(client, prompts, keychain, config, args) {
1222
1276
  }
1223
1277
  function registerLogin(program, makeClient2, loadConfig2, prompts, keychain, persist) {
1224
1278
  program.command("login").description("connect your wardrobe with a passwordless magic-link (no password, ever).").option("--email <email>", "your email (skips the prompt)").action(async (opts) => {
1225
- const json = Boolean(program.opts().json);
1279
+ const { json } = resolveOutput(program);
1226
1280
  const envToken = process.env.BT_MAGIC_TOKEN?.trim() || void 0;
1227
1281
  const res = await runLogin(makeClient2(), prompts, keychain, loadConfig2(), {
1228
1282
  email: opts.email,
@@ -1310,7 +1364,7 @@ async function runDrop(args, exec) {
1310
1364
  }
1311
1365
  function registerDrop(program, exec) {
1312
1366
  program.command("drop", { hidden: true }).argument("<sub>", "create | verify | promote | retire").option("--motif <slug>", "motif slug (create)").option("--drop-id <id>", "the drop id").option("--stage <stage>", "expected stage for verify (next|main)").description("operator: manage a drop's lifecycle (create/verify/promote/retire).").action(async (sub, opts) => {
1313
- const json = Boolean(program.opts().json);
1367
+ const { json } = resolveOutput(program);
1314
1368
  const operator = process.env.BT_OPERATOR === "1";
1315
1369
  process.stdout.write(working(`drop ${sub}\u2026`) + "\n");
1316
1370
  const res = await runDrop(
@@ -1351,10 +1405,7 @@ function runInit(args) {
1351
1405
  }
1352
1406
  function registerInit(program) {
1353
1407
  program.command("init").description("first-run welcome + the 3-command happy path.").action(async () => {
1354
- const gOpts = program.opts();
1355
- const json = Boolean(gOpts.json);
1356
- const { makeBrand: makeBrand2 } = await import("./brand-TMOYK6QE.js");
1357
- const brand = json ? makeBrand2({ noColor: true }) : makeBrand2({ noColor: gOpts.color === false });
1408
+ const { json, brand } = resolveOutput(program);
1358
1409
  printResult(runInit({ json, brand }), json);
1359
1410
  });
1360
1411
  }
@@ -1386,9 +1437,7 @@ async function runBuy(client, config, args, deps) {
1386
1437
  }
1387
1438
  function registerBuy(program, makeClient2, loadConfig2) {
1388
1439
  program.command("buy").argument("<handle>", "the product handle/slug").option("--size <size>", "garment size").option("--colour <colour>", "colour, e.g. white / black").description("stage + checkout in one \u2014 reserve, then open browser checkout. cards never touch the terminal.").action(async (handle, opts) => {
1389
- const gOpts = program.opts();
1390
- const json = Boolean(gOpts.json);
1391
- const brand = json ? makeBrand({ noColor: true }) : makeBrand({ noColor: gOpts.color === false });
1440
+ const { json, brand } = resolveOutput(program);
1392
1441
  const { makePrompts: makePrompts2 } = await import("./prompts-GTC5VYM6.js");
1393
1442
  const { openCheckout } = await import("./browser-EO5EQUXQ.js");
1394
1443
  const { pollUntilPlaced } = await import("./poll-O3VUD5WV.js");
@@ -1446,7 +1495,7 @@ function runConfig(args, deps) {
1446
1495
  }
1447
1496
  function registerConfig(program) {
1448
1497
  program.command("config").argument("[sub]", "get | set", "get").argument("[key]", "config key").argument("[value]", "value (for set)").description("view or set local prefs (email, region, defaults). the auth token is never stored here.").action(async (sub, key, value) => {
1449
- const json = Boolean(program.opts().json);
1498
+ const { json } = resolveOutput(program);
1450
1499
  const { loadConfig: loadConfig2, saveConfig: saveConfig2 } = await import("./config-FXGCWC6Y.js");
1451
1500
  const res = runConfig({ sub, key, value, json }, { load: loadConfig2, save: saveConfig2 });
1452
1501
  printResult(res, json);
@@ -1480,7 +1529,7 @@ function gatherDoctorSnapshot(opts = {}) {
1480
1529
  } catch {
1481
1530
  }
1482
1531
  return {
1483
- btVersion: "0.1.0",
1532
+ btVersion: VERSION,
1484
1533
  node: process.versions.node,
1485
1534
  platform: `${process.platform}/${process.arch}`,
1486
1535
  storeUrl: baseUrl,
@@ -1558,7 +1607,6 @@ function registerReport(program, makeClient2) {
1558
1607
  }
1559
1608
 
1560
1609
  // src/bin.ts
1561
- var VERSION = "0.1.0";
1562
1610
  function makeClient() {
1563
1611
  const { baseUrl, publishableKey } = resolveClientConfig();
1564
1612
  const jwt = resolveBridgeToken(process.env, getToken()) ?? void 0;