@binaryandthread/cli 0.1.6 → 0.1.9
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 +8 -3
- package/dist/bin.js +110 -18
- package/dist/bin.js.map +1 -1
- package/dist/{browser-EO5EQUXQ.js → browser-WCNC4AWS.js} +22 -3
- package/dist/browser-WCNC4AWS.js.map +1 -0
- package/dist/{chunk-CA3GGJGF.js → chunk-RGXH3EZ6.js} +5 -1
- package/dist/chunk-RGXH3EZ6.js.map +1 -0
- package/dist/{interactive-HKGPS4PX.js → interactive-2YROZBIN.js} +2 -2
- package/package.json +1 -1
- package/dist/browser-EO5EQUXQ.js.map +0 -1
- package/dist/chunk-CA3GGJGF.js.map +0 -1
- /package/dist/{interactive-HKGPS4PX.js.map → interactive-2YROZBIN.js.map} +0 -0
package/README.md
CHANGED
|
@@ -23,14 +23,19 @@ A bare `bt` may collide with another binary on your `PATH`. If it does, this pac
|
|
|
23
23
|
**`btn`** as an alias, `btn browse` works identically. (`npx @binaryandthread/cli …` is always the
|
|
24
24
|
unambiguous form.)
|
|
25
25
|
|
|
26
|
+
> **The only package is the scoped `@binaryandthread/cli`** (signed, 2FA-published). `npx bnt`,
|
|
27
|
+
> `npx bt`, `npx btn`, and `npx binaryandthread` are **different, unscoped packages owned by
|
|
28
|
+
> strangers** — never run them. Always install the full scoped name.
|
|
29
|
+
|
|
26
30
|
## Commands
|
|
27
31
|
|
|
28
32
|
| command | what it does |
|
|
29
33
|
| --- | --- |
|
|
30
|
-
| `bt browse` | the catalog (
|
|
34
|
+
| `bt browse` | the catalog as a numbered table (`bt stage <n>`); `--pick` opts into the ↑/↓ picker; `--json` for scripts |
|
|
31
35
|
| `bt search <q>` | filter the catalog |
|
|
32
|
-
| `bt view <
|
|
33
|
-
| `bt stage <
|
|
36
|
+
| `bt view <n\|slug>` | one piece, specs + mockup (`--colour black` renders the black row) |
|
|
37
|
+
| `bt stage <n\|slug>` | add to your cart (`--size`, `--colour`) |
|
|
38
|
+
| `bt blame <n\|slug>` | the design's lore + sources (git blame, for motifs) |
|
|
34
39
|
| `bt cart` / `bt unstage <ref>` | show / remove a line |
|
|
35
40
|
| `bt checkout` | mint a checkout, hand off to the browser to pay, then poll |
|
|
36
41
|
| `bt order <id>` / `bt log` | track an order / your order history (when logged in) |
|
package/dist/bin.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
configDir,
|
|
3
4
|
configPath,
|
|
4
5
|
forgetConfig,
|
|
5
6
|
loadConfig,
|
|
@@ -48,7 +49,7 @@ import {
|
|
|
48
49
|
versionLine,
|
|
49
50
|
warn,
|
|
50
51
|
working
|
|
51
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-RGXH3EZ6.js";
|
|
52
53
|
import {
|
|
53
54
|
__require
|
|
54
55
|
} from "./chunk-7OCVIDC7.js";
|
|
@@ -77,7 +78,7 @@ function makeLogger(debug, write = (s) => process.stderr.write(s)) {
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
// src/version.ts
|
|
80
|
-
var VERSION = "0.1.
|
|
81
|
+
var VERSION = "0.1.9";
|
|
81
82
|
|
|
82
83
|
// src/eggs.ts
|
|
83
84
|
function teapot() {
|
|
@@ -213,16 +214,99 @@ function isMainModule(argv1, moduleUrl, realpath = realpathSync) {
|
|
|
213
214
|
}
|
|
214
215
|
}
|
|
215
216
|
|
|
216
|
-
// src/
|
|
217
|
+
// src/update-notice.ts
|
|
217
218
|
import * as fs from "fs";
|
|
218
|
-
import * as os from "os";
|
|
219
219
|
import * as path from "path";
|
|
220
|
+
var CACHE_FILE = "update-check.json";
|
|
221
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
222
|
+
var REGISTRY_LATEST = "https://registry.npmjs.org/@binaryandthread/cli/latest";
|
|
223
|
+
var FETCH_TIMEOUT_MS = 2e3;
|
|
224
|
+
function parseSemver(v) {
|
|
225
|
+
const m = /^(\d+)\.(\d+)\.(\d+)/.exec((v ?? "").trim());
|
|
226
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
227
|
+
}
|
|
228
|
+
function semverGt(a, b) {
|
|
229
|
+
const pa = parseSemver(a);
|
|
230
|
+
const pb = parseSemver(b);
|
|
231
|
+
if (!pa || !pb) return false;
|
|
232
|
+
for (let i = 0; i < 3; i++) {
|
|
233
|
+
if (pa[i] > pb[i]) return true;
|
|
234
|
+
if (pa[i] < pb[i]) return false;
|
|
235
|
+
}
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
function updateNoticeLine(current, latest) {
|
|
239
|
+
if (!latest || !semverGt(latest, current)) return null;
|
|
240
|
+
return `\u2699 a newer bt is out (${latest}). update \u2192 npm i -g @binaryandthread/cli@latest`;
|
|
241
|
+
}
|
|
242
|
+
function isUpdateCheckSuppressed(env, argv, stderrIsTTY) {
|
|
243
|
+
if (!stderrIsTTY) return true;
|
|
244
|
+
if (argv.includes("--json")) return true;
|
|
245
|
+
if (env.CI || env.BT_NO_UPDATE_NOTIFIER || env.NO_UPDATE_NOTIFIER) return true;
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
function maybeNotifyUpdate(deps) {
|
|
249
|
+
if (isUpdateCheckSuppressed(deps.env, deps.argv, deps.stderrIsTTY)) return;
|
|
250
|
+
const now = deps.now ?? Date.now();
|
|
251
|
+
const read = deps.readCache ?? readUpdateCache;
|
|
252
|
+
const refresh = deps.refresh ?? ((n) => void fetchLatestIntoCache(n));
|
|
253
|
+
const cache = read();
|
|
254
|
+
const line = updateNoticeLine(deps.current, cache?.latest);
|
|
255
|
+
if (line) deps.write(line + "\n");
|
|
256
|
+
if (!cache || now - cache.lastCheck > CHECK_INTERVAL_MS) {
|
|
257
|
+
refresh(now);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function cachePath() {
|
|
261
|
+
return path.join(configDir(), CACHE_FILE);
|
|
262
|
+
}
|
|
263
|
+
function readUpdateCache() {
|
|
264
|
+
try {
|
|
265
|
+
const j = JSON.parse(fs.readFileSync(cachePath(), "utf8"));
|
|
266
|
+
if (j && typeof j.latest === "string" && typeof j.lastCheck === "number") {
|
|
267
|
+
return { latest: j.latest, lastCheck: j.lastCheck };
|
|
268
|
+
}
|
|
269
|
+
} catch {
|
|
270
|
+
}
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
function writeUpdateCache(c) {
|
|
274
|
+
try {
|
|
275
|
+
fs.mkdirSync(configDir(), { recursive: true });
|
|
276
|
+
const tmp = cachePath() + ".tmp";
|
|
277
|
+
fs.writeFileSync(tmp, JSON.stringify(c), "utf8");
|
|
278
|
+
fs.renameSync(tmp, cachePath());
|
|
279
|
+
} catch {
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async function fetchLatestIntoCache(now) {
|
|
283
|
+
try {
|
|
284
|
+
const ctrl = new AbortController();
|
|
285
|
+
const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
|
|
286
|
+
try {
|
|
287
|
+
const res = await fetch(REGISTRY_LATEST, { signal: ctrl.signal });
|
|
288
|
+
if (!res.ok) return;
|
|
289
|
+
const data = await res.json();
|
|
290
|
+
if (typeof data.version === "string") {
|
|
291
|
+
writeUpdateCache({ lastCheck: now, latest: data.version });
|
|
292
|
+
}
|
|
293
|
+
} finally {
|
|
294
|
+
clearTimeout(timer);
|
|
295
|
+
}
|
|
296
|
+
} catch {
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// src/keychain.ts
|
|
301
|
+
import * as fs2 from "fs";
|
|
302
|
+
import * as os from "os";
|
|
303
|
+
import * as path2 from "path";
|
|
220
304
|
var KEYCHAIN_SERVICE = "binaryandthread/bt";
|
|
221
305
|
var KEYCHAIN_ACCOUNT = "jwt";
|
|
222
306
|
function fallbackTokenPath() {
|
|
223
307
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
224
|
-
const base = xdg && xdg.trim() ? xdg :
|
|
225
|
-
return
|
|
308
|
+
const base = xdg && xdg.trim() ? xdg : path2.join(os.homedir(), ".config");
|
|
309
|
+
return path2.join(base, "bt", "token");
|
|
226
310
|
}
|
|
227
311
|
function defaultMakeEntry(service, account) {
|
|
228
312
|
try {
|
|
@@ -239,7 +323,7 @@ function setToken(jwt, deps = {}) {
|
|
|
239
323
|
try {
|
|
240
324
|
entry.setPassword(jwt);
|
|
241
325
|
try {
|
|
242
|
-
|
|
326
|
+
fs2.rmSync(fallbackTokenPath(), { force: true });
|
|
243
327
|
} catch {
|
|
244
328
|
}
|
|
245
329
|
return;
|
|
@@ -247,10 +331,10 @@ function setToken(jwt, deps = {}) {
|
|
|
247
331
|
}
|
|
248
332
|
}
|
|
249
333
|
const target = fallbackTokenPath();
|
|
250
|
-
|
|
334
|
+
fs2.mkdirSync(path2.dirname(target), { recursive: true });
|
|
251
335
|
const tmp = target + ".tmp";
|
|
252
|
-
|
|
253
|
-
|
|
336
|
+
fs2.writeFileSync(tmp, jwt, { encoding: "utf8", mode: 384 });
|
|
337
|
+
fs2.renameSync(tmp, target);
|
|
254
338
|
}
|
|
255
339
|
function getToken(deps = {}) {
|
|
256
340
|
const make = deps.makeEntry ?? defaultMakeEntry;
|
|
@@ -265,7 +349,7 @@ function getToken(deps = {}) {
|
|
|
265
349
|
}
|
|
266
350
|
}
|
|
267
351
|
try {
|
|
268
|
-
const raw =
|
|
352
|
+
const raw = fs2.readFileSync(fallbackTokenPath(), "utf8").trim();
|
|
269
353
|
return raw || null;
|
|
270
354
|
} catch {
|
|
271
355
|
return null;
|
|
@@ -281,7 +365,7 @@ function deleteToken(deps = {}) {
|
|
|
281
365
|
}
|
|
282
366
|
}
|
|
283
367
|
try {
|
|
284
|
-
|
|
368
|
+
fs2.rmSync(fallbackTokenPath(), { force: true });
|
|
285
369
|
} catch {
|
|
286
370
|
}
|
|
287
371
|
}
|
|
@@ -356,7 +440,7 @@ function registerBrowse(program, makeClient2, loadConfig2) {
|
|
|
356
440
|
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) => {
|
|
357
441
|
const { json, brand } = resolveOutput(program);
|
|
358
442
|
if (opts.pick && !json && brand.color && process.stdout.isTTY && process.stdin.isTTY) {
|
|
359
|
-
const { runInteractiveBrowse } = await import("./interactive-
|
|
443
|
+
const { runInteractiveBrowse } = await import("./interactive-2YROZBIN.js");
|
|
360
444
|
const handled = await runInteractiveBrowse(makeClient2(), loadConfig2(), { branch: opts.branch, brand });
|
|
361
445
|
if (handled) return;
|
|
362
446
|
}
|
|
@@ -686,7 +770,7 @@ function registerCheckout(program, makeClient2, loadConfig2) {
|
|
|
686
770
|
const action = async (opts) => {
|
|
687
771
|
const { json, brand } = resolveOutput(program);
|
|
688
772
|
const { makePrompts: makePrompts2 } = await import("./prompts-DDVV5RE6.js");
|
|
689
|
-
const { openCheckout } = await import("./browser-
|
|
773
|
+
const { openCheckout } = await import("./browser-WCNC4AWS.js");
|
|
690
774
|
const { pollUntilPlaced } = await import("./poll-O3VUD5WV.js");
|
|
691
775
|
const { saveConfig: saveConfig2 } = await import("./config-FXGCWC6Y.js");
|
|
692
776
|
const client = makeClient2();
|
|
@@ -1482,7 +1566,7 @@ function registerBuy(program, makeClient2, loadConfig2) {
|
|
|
1482
1566
|
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").description("stage + checkout in one \u2014 reserve, then open browser checkout. cards never touch the terminal.").action(async (handle, opts) => {
|
|
1483
1567
|
const { json, brand } = resolveOutput(program);
|
|
1484
1568
|
const { makePrompts: makePrompts2 } = await import("./prompts-DDVV5RE6.js");
|
|
1485
|
-
const { openCheckout } = await import("./browser-
|
|
1569
|
+
const { openCheckout } = await import("./browser-WCNC4AWS.js");
|
|
1486
1570
|
const { pollUntilPlaced } = await import("./poll-O3VUD5WV.js");
|
|
1487
1571
|
const { saveConfig: saveConfig2 } = await import("./config-FXGCWC6Y.js");
|
|
1488
1572
|
const client = makeClient2();
|
|
@@ -1553,7 +1637,7 @@ function registerConfig(program) {
|
|
|
1553
1637
|
}
|
|
1554
1638
|
|
|
1555
1639
|
// src/commands/doctor.ts
|
|
1556
|
-
import * as
|
|
1640
|
+
import * as fs3 from "fs";
|
|
1557
1641
|
function runDoctor(s, json) {
|
|
1558
1642
|
const auth = !s.loggedIn ? "guest (not logged in)" : s.tokenExpired ? `logged in but token EXPIRED${s.tokenExpiresAt ? ` (${s.tokenExpiresAt})` : ""} \u2014 run bt login` : `logged in${s.tokenExpiresAt ? ` \xB7 token valid until ${s.tokenExpiresAt}` : ""}`;
|
|
1559
1643
|
const lines = [
|
|
@@ -1575,7 +1659,7 @@ function gatherDoctorSnapshot(opts = {}) {
|
|
|
1575
1659
|
const exp = jwtExpiry(token);
|
|
1576
1660
|
let configExists = false;
|
|
1577
1661
|
try {
|
|
1578
|
-
configExists =
|
|
1662
|
+
configExists = fs3.existsSync(configPath());
|
|
1579
1663
|
} catch {
|
|
1580
1664
|
}
|
|
1581
1665
|
return {
|
|
@@ -1686,7 +1770,7 @@ async function runBlame(client, config, args) {
|
|
|
1686
1770
|
return { lines, json: args.json ? { handle, lore } : void 0 };
|
|
1687
1771
|
}
|
|
1688
1772
|
function registerBlame(program, makeClient2, loadConfig2) {
|
|
1689
|
-
program.command("blame"
|
|
1773
|
+
program.command("blame").argument("[slug]", "a design \u2014 a slug, a bt browse number, or a fuzzy hint").description("who's responsible for a design \u2014 its lore + sources (git blame, for motifs).").action(async (slug) => {
|
|
1690
1774
|
const { json, brand } = resolveOutput(program);
|
|
1691
1775
|
const res = await runBlame(makeClient2(), loadConfig2(), { handle: slug, json, brand });
|
|
1692
1776
|
printResult(res, json);
|
|
@@ -1802,6 +1886,14 @@ if (isMainModule(process.argv[1], import.meta.url)) {
|
|
|
1802
1886
|
if (out) process.stdout.write(out + "\n");
|
|
1803
1887
|
if (err) process.stderr.write(err + "\n");
|
|
1804
1888
|
process.exitCode = code;
|
|
1889
|
+
}).finally(() => {
|
|
1890
|
+
maybeNotifyUpdate({
|
|
1891
|
+
current: VERSION,
|
|
1892
|
+
env: process.env,
|
|
1893
|
+
argv: process.argv,
|
|
1894
|
+
stderrIsTTY: Boolean(process.stderr.isTTY),
|
|
1895
|
+
write: (s) => process.stderr.write(s)
|
|
1896
|
+
});
|
|
1805
1897
|
});
|
|
1806
1898
|
}
|
|
1807
1899
|
export {
|