@binaryandthread/cli 0.4.10 → 0.4.12

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
@@ -58,7 +58,7 @@ Listing them would defeat the point.
58
58
  | `bt browse` | the catalog as a numbered table (`bt stage <n>`); `--pick` opts into the ↑/↓ picker; `--json` for scripts |
59
59
  | `bt search <q>` | filter the catalog |
60
60
  | `bt view <n\|slug>` | one piece, specs + mockup (`--colour black` renders the black row; `--fit oversize` shows the other fit of a two-fit design) |
61
- | `bt stage <n\|slug>` | add to your cart (`--size`, `--colour`, `--fit regular\|oversize`; a design sold in both fits needs `--fit`, or `bt config set defaultFit …`) |
61
+ | `bt stage <n\|slug>` | add to your cart (`--size`, `--colour`, `--fit regular\|oversize`; `--size` is required unless `bt config set defaultSize M`; a design sold in both fits needs `--fit`, or `bt config set defaultFit …`; one design per command, a two-word colour is `--colour coal` or quoted) |
62
62
  | `bt blame <n\|slug>` | the design's lore + sources (git blame, for motifs) |
63
63
  | `bt cart` / `bt unstage <ref>` | show / remove a line |
64
64
  | `bt checkout` | mint a checkout, hand off to the browser to pay, then poll |
@@ -111,7 +111,7 @@ skips the interactive confirm so it never hangs in CI. Field shapes (stable, saf
111
111
  | --- | --- |
112
112
  | `bt browse` / `bt search` | `[{ id, handle, title, price, price_from, currency, colours[], fits[], channel_stage }]` (`price` is the cheapest fit when `price_from`) |
113
113
  | `bt view <handle>` | `{ id, handle, title, fits[], fit, colours[], unavailable_colours[], specs, variants: [{ id, fit, colour, size, price, currency }] }` |
114
- | `bt cart` | `{ cart_id, items: [{ id, title, colour, qty, total }], total, total_excludes_shipping }` |
114
+ | `bt cart` | `{ cart_id, items: [{ id, title, colour, qty, total }], total, total_excludes_shipping }`; a cart that became an order answers `{ cart_id: null, items: [], completed: true }` and clears the saved id |
115
115
  | `bt checkout` | `{ status: "placed"\|"timeout"\|"expired", order_id?, display_id?, total?, currency?, checkout_url? }` |
116
116
  | `bt region` | `{ region, currency, matched, home }` · `--list` → `{ regions: [{ currency, countries[] }] }` |
117
117
  | `bt whoami` | `{ authenticated, tier, commits, standing_pct, token_expires_at, token_expired }` |
package/dist/bin.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  forgetConfig,
6
6
  loadConfig,
7
7
  saveConfig
8
- } from "./chunk-T7MFWTB7.js";
8
+ } from "./chunk-IUN3OOYY.js";
9
9
  import {
10
10
  makePrompts
11
11
  } from "./chunk-AAN4VWP3.js";
@@ -38,6 +38,7 @@ import {
38
38
  labelVariantTitle,
39
39
  lineColour,
40
40
  ok,
41
+ oneDesignPerCommand,
41
42
  orderStage,
42
43
  priceLine,
43
44
  printResult,
@@ -58,7 +59,7 @@ import {
58
59
  versionLine,
59
60
  warn,
60
61
  working
61
- } from "./chunk-PVKB77IE.js";
62
+ } from "./chunk-YNBNKCCN.js";
62
63
  import {
63
64
  __require
64
65
  } from "./chunk-7OCVIDC7.js";
@@ -107,7 +108,7 @@ function makeLogger(debug, write = (s) => process.stderr.write(s)) {
107
108
  }
108
109
 
109
110
  // src/version.ts
110
- var VERSION = "0.4.10";
111
+ var VERSION = "0.4.12";
111
112
 
112
113
  // src/eggs.ts
113
114
  function teapot() {
@@ -195,7 +196,7 @@ function manPage(version) {
195
196
  " init first-run welcome + the happy path",
196
197
  " browse list what's in stock",
197
198
  " view render one piece (image or ASCII) + specs",
198
- " stage reserve stock (like git add)",
199
+ " stage reserve stock (like git add); --size required unless bt config set defaultSize",
199
200
  " commit turn your staged items into an order",
200
201
  " buy stage + commit in one",
201
202
  " log your order history as a git log (--graph, --oneline)",
@@ -527,7 +528,7 @@ function registerBrowse(program, makeClient2, loadConfig2) {
527
528
  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("--pick", "opt into the arrow-key picker (default is the numbered table \u2014 use bt stage <n>)").option("--plain", "force the static table (the default; kept for back-compat)").action(async (opts) => {
528
529
  const { json, brand } = resolveOutput(program);
529
530
  if (opts.pick && !json && brand.color && process.stdout.isTTY && process.stdin.isTTY) {
530
- const { runInteractiveBrowse } = await import("./interactive-B5TALADG.js");
531
+ const { runInteractiveBrowse } = await import("./interactive-JDUPAMLW.js");
531
532
  const handled = await runInteractiveBrowse(makeClient2(), loadConfig2(), { branch: opts.branch, brand });
532
533
  if (handled) return;
533
534
  }
@@ -730,6 +731,7 @@ function registerSearch(program, makeClient2, loadConfig2) {
730
731
 
731
732
  // src/commands/cart.ts
732
733
  var EMPTY_STATE = "no commits yet. your wardrobe is a fresh repo.";
734
+ var COMMITTED_LINE = "that cart was committed as an order, so it is no longer yours to edit.";
733
735
  async function runCart(client, config, args) {
734
736
  if (!config.cart_id) {
735
737
  return {
@@ -740,6 +742,14 @@ async function runCart(client, config, args) {
740
742
  const style = brandOf(args);
741
743
  try {
742
744
  const cart = await client.getCart(config.cart_id);
745
+ if (cart.completed_at) {
746
+ return {
747
+ lines: [warn(COMMITTED_LINE), empty(EMPTY_STATE), " start one \u2192 bt browse"],
748
+ // the id is dead: clear it, so the next bt stage starts a fresh cart without a detour
749
+ save: { cart_id: null },
750
+ json: args.json ? { cart_id: null, items: [], completed: true } : void 0
751
+ };
752
+ }
743
753
  const lines = [];
744
754
  if (!cart.items || cart.items.length === 0) {
745
755
  lines.push(empty(EMPTY_STATE));
@@ -795,6 +805,10 @@ function registerCart(program, makeClient2, loadConfig2) {
795
805
  program.command("cart").description("show your current cart \u2014 the authoritative server state.").action(async () => {
796
806
  const { json, brand } = resolveOutput(program);
797
807
  const res = await runCart(makeClient2(), loadConfig2(), { json, brand });
808
+ if (res.save) {
809
+ const { saveConfig: saveConfig2 } = await import("./config-PWI2G2O5.js");
810
+ saveConfig2(res.save);
811
+ }
798
812
  printResult(res, json);
799
813
  });
800
814
  }
@@ -815,6 +829,14 @@ function heartbeatLine(attempt, opts = {}) {
815
829
  `still waiting for the browser hand-off, ${elapsedS}s in. ctrl-c is safe; resume with bt checkout --resume`
816
830
  );
817
831
  }
832
+ function committedResult(args) {
833
+ return {
834
+ lines: [warn(COMMITTED_LINE), " see the order \u2192 bt log"],
835
+ save: { cart_id: null },
836
+ json: args.json ? { status: "completed", cart_id: null } : void 0,
837
+ exitCode: 0
838
+ };
839
+ }
818
840
  async function runCheckout(client, config, args, deps) {
819
841
  const cartId = config.cart_id;
820
842
  if (!cartId) {
@@ -847,6 +869,9 @@ async function runCheckout(client, config, args, deps) {
847
869
  }
848
870
  throw e;
849
871
  }
872
+ if (cart.completed_at) {
873
+ return committedResult(args);
874
+ }
850
875
  if (!cart.items || cart.items.length === 0) {
851
876
  return {
852
877
  lines: [
@@ -886,7 +911,15 @@ async function runCheckout(client, config, args, deps) {
886
911
  };
887
912
  }
888
913
  const email = (config.email ?? "").trim();
889
- const mint = await client.mintCheckout(cartId, email || void 0, "cli");
914
+ let mint;
915
+ try {
916
+ mint = await client.mintCheckout(cartId, email || void 0, "cli");
917
+ } catch (e) {
918
+ if (e instanceof StoreApiError && e.status === 409) {
919
+ return committedResult(args);
920
+ }
921
+ throw e;
922
+ }
890
923
  const { checkout_url, session_id } = mint;
891
924
  const save = email ? { email } : {};
892
925
  const currency = mint.currency ?? cart.currency_code ?? region.currency_code;
@@ -965,7 +998,7 @@ async function runCheckout(client, config, args, deps) {
965
998
  }
966
999
  const lines = [];
967
1000
  if (outcome.status === "placed") {
968
- const displayId = await resolveDisplayId(client, outcome.order_id, email);
1001
+ const displayId = outcome.summary?.display_id ?? await resolveDisplayId(client, outcome.order_id, email);
969
1002
  lines.push("");
970
1003
  lines.push(ok("deploy successful."));
971
1004
  lines.push(` committed to wardrobe@main`);
@@ -976,7 +1009,7 @@ async function runCheckout(client, config, args, deps) {
976
1009
  if (settled?.discount_total) {
977
1010
  const codes = settled.discount_codes.length ? ` (${settled.discount_codes.join(", ")})` : "";
978
1011
  lines.push(dim(style, ` discount${codes} \u2212${fmtPrice(settled.discount_total, finalCurrency)}`));
979
- } else if (mint.discount && mint.discount.amount > 0) {
1012
+ } else if (!settled && mint.discount && mint.discount.amount > 0) {
980
1013
  lines.push(dim(style, ` dev channel \u2212${fmtPrice(mint.discount.amount, currency)} (${mint.discount.pct}% \u2014 the terminal's cut for you)`));
981
1014
  }
982
1015
  lines.push(` total ${cyan(style, fmtPrice(finalTotal, finalCurrency))}${taxIncl ? " (incl. VAT)" : ""}`);
@@ -991,7 +1024,9 @@ async function runCheckout(client, config, args, deps) {
991
1024
  }
992
1025
  return {
993
1026
  lines,
994
- save,
1027
+ // TB252 (plan-check 2): the cart is an order now, so its id leaves the config. Left behind, it made
1028
+ // `bt cart` list the paid items as still staged.
1029
+ save: { ...save, cart_id: null },
995
1030
  json: args.json ? {
996
1031
  status: "placed",
997
1032
  order_id: outcome.order_id,
@@ -1036,6 +1071,9 @@ async function resolveDisplayId(client, orderId, email) {
1036
1071
  if (!orderId) {
1037
1072
  return "\u2014";
1038
1073
  }
1074
+ if (!email) {
1075
+ return orderId;
1076
+ }
1039
1077
  try {
1040
1078
  const looked = await client.getOrder(orderId, email);
1041
1079
  const display = looked?.order?.display_id ?? looked?.display_id;
@@ -1060,7 +1098,7 @@ function registerCheckout(program, makeClient2, loadConfig2) {
1060
1098
  const { makePrompts: makePrompts2 } = await import("./prompts-DDVV5RE6.js");
1061
1099
  const { openCheckout } = await import("./browser-WCNC4AWS.js");
1062
1100
  const { pollUntilPlaced } = await import("./poll-II3TVJLK.js");
1063
- const { saveConfig: saveConfig2 } = await import("./config-L4X7G563.js");
1101
+ const { saveConfig: saveConfig2 } = await import("./config-PWI2G2O5.js");
1064
1102
  const client = makeClient2();
1065
1103
  const print = json ? () => {
1066
1104
  } : (line) => process.stdout.write(line + "\n");
@@ -1314,6 +1352,13 @@ async function runStatus(client, config, args) {
1314
1352
  const style = brandOf(args);
1315
1353
  try {
1316
1354
  const cart = await client.getCart(config.cart_id);
1355
+ if (cart.completed_at) {
1356
+ return {
1357
+ lines: ["on branch main", warn(COMMITTED_LINE), empty(FRESH_REPO), " nothing staged. clean tree \u2192 bt browse"],
1358
+ save: { cart_id: null },
1359
+ json: args.json ? { branch: "main", staged: [], clean: true, completed: true } : void 0
1360
+ };
1361
+ }
1317
1362
  const items = cart.items ?? [];
1318
1363
  const lines = ["on branch main"];
1319
1364
  if (items.length === 0) {
@@ -1352,6 +1397,10 @@ function registerStatus(program, makeClient2, loadConfig2) {
1352
1397
  program.command("status").description("working-tree state: what's staged, what's in flight.").action(async () => {
1353
1398
  const { json, brand } = resolveOutput(program);
1354
1399
  const res = await runStatus(makeClient2(), loadConfig2(), { json, brand });
1400
+ if (res.save) {
1401
+ const { saveConfig: saveConfig2 } = await import("./config-PWI2G2O5.js");
1402
+ saveConfig2(res.save);
1403
+ }
1355
1404
  printResult(res, json);
1356
1405
  });
1357
1406
  }
@@ -1811,6 +1860,7 @@ async function runBuy(client, config, args, deps) {
1811
1860
  if (!cartId) {
1812
1861
  return staged;
1813
1862
  }
1863
+ deps.saveConfig(staged.save);
1814
1864
  const checked = await runCheckout(
1815
1865
  client,
1816
1866
  { ...config, cart_id: cartId },
@@ -1827,12 +1877,16 @@ async function runBuy(client, config, args, deps) {
1827
1877
  };
1828
1878
  }
1829
1879
  function registerBuy(program, makeClient2, loadConfig2) {
1830
- program.command("buy").argument("<handle>", "the design: a slug, a bt browse number (3), or a fuzzy hint (termnl)").option("--size <size>", "garment size").option("--colour <colour>", "colour, e.g. white / black").option("--fit <fit>", "regular or oversize (required when a design comes in both)").description("stage + checkout in one \u2014 reserve, then open browser checkout. cards never touch the terminal.").action(async (handle, opts) => {
1880
+ program.command("buy").argument("<handle>", "the design: a slug, a bt browse number (3), or a fuzzy hint (termnl)").option("--size <size>", "garment size (required unless bt config set defaultSize)").option("--colour <colour>", "colour, e.g. white / black").option("--fit <fit>", "regular or oversize (required when a design comes in both)").description("stage + checkout in one \u2014 reserve, then open browser checkout. cards never touch the terminal.").allowExcessArguments().action(async (handle, opts, cmd) => {
1831
1881
  const { json, brand } = resolveOutput(program);
1882
+ if (cmd.args.length > 1) {
1883
+ printResult(oneDesignPerCommand(), json);
1884
+ return;
1885
+ }
1832
1886
  const { makePrompts: makePrompts2 } = await import("./prompts-DDVV5RE6.js");
1833
1887
  const { openCheckout } = await import("./browser-WCNC4AWS.js");
1834
1888
  const { pollUntilPlaced } = await import("./poll-II3TVJLK.js");
1835
- const { saveConfig: saveConfig2 } = await import("./config-L4X7G563.js");
1889
+ const { saveConfig: saveConfig2 } = await import("./config-PWI2G2O5.js");
1836
1890
  const client = makeClient2();
1837
1891
  const print = json ? () => {
1838
1892
  } : (line) => process.stdout.write(line + "\n");
@@ -1892,7 +1946,7 @@ function runConfig(args, deps) {
1892
1946
  function registerConfig(program) {
1893
1947
  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) => {
1894
1948
  const { json } = resolveOutput(program);
1895
- const { loadConfig: loadConfig2, saveConfig: saveConfig2 } = await import("./config-L4X7G563.js");
1949
+ const { loadConfig: loadConfig2, saveConfig: saveConfig2 } = await import("./config-PWI2G2O5.js");
1896
1950
  const res = runConfig({ sub, key, value, json }, { load: loadConfig2, save: saveConfig2 });
1897
1951
  printResult(res, json);
1898
1952
  });
@@ -2059,6 +2113,10 @@ function buildProgram() {
2059
2113
  "the binary & thread terminal store. techno-minimalist clothing & lifestyle, bought with the ceremony of a deploy.\nprimary invocation: npx @binaryandthread/cli (a bare `bt` may collide with another binary)"
2060
2114
  ).version(versionLine(VERSION), "-v, --version", "print the version").option("--json", "machine-readable output", false).option("--no-color", "disable ANSI colour + inline images (also honours NO_COLOR)").option("--debug, --verbose", "trace requests to stderr (redacted) \u2014 for bug reports (d15)").option("--region <cc>", "ship-to country for this command (ISO-2, overrides your saved region)").showSuggestionAfterError();
2061
2115
  program.addHelpText("beforeAll", "\n bt \u2014 the binary & thread terminal store. new here? \u2192 bt init\n");
2116
+ program.addHelpText(
2117
+ "after",
2118
+ "\nexamples:\n bt browse\n bt stage konami-code --size M --colour coal\n bt buy konami-code --size M --colour coal\n bt stage --help (every command has its own)\n"
2119
+ );
2062
2120
  program.command("brew").alias("teapot").argument("[what]", "what to brew").description("we make clothing, not coffee.").action(() => {
2063
2121
  process.stdout.write(teapot() + "\n");
2064
2122
  });