@tmturtle/cli 1.0.0 → 1.0.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Trademark Turtle CLI Changelog
2
2
 
3
+ ## v1.0.2 - 2026-07-22
4
+
5
+ - API keys entered through `tt auth set` remain hidden in real terminals.
6
+
7
+ ## v1.0.1 - 2026-07-22
8
+
9
+ - `tt auth set` now opens a hidden interactive prompt, while `--stdin` remains
10
+ available for scripts and agent workflows.
11
+
3
12
  ## v1.0.0 - 2026-07-22
4
13
 
5
14
  - Search United States trademarks with Exact, Multi, Split, and Wildcard modes.
package/README.md CHANGED
@@ -12,7 +12,13 @@ The CLI requires Bun 1.3.5 or newer.
12
12
  bun add --global @tmturtle/cli
13
13
  ```
14
14
 
15
- Store a Trademark Turtle API key in macOS Keychain:
15
+ Store a Trademark Turtle API key interactively in macOS Keychain:
16
+
17
+ ```sh
18
+ tt auth set
19
+ ```
20
+
21
+ For non-interactive setup:
16
22
 
17
23
  ```sh
18
24
  printf '%s' "$TMTURTLE_API_KEY" | tt auth set --stdin
package/dist/tt.js CHANGED
@@ -1287,7 +1287,7 @@ function createTmturtleClient({ apiKey, baseUrl }) {
1287
1287
  // package.json
1288
1288
  var package_default = {
1289
1289
  name: "@tmturtle/cli",
1290
- version: "1.0.0",
1290
+ version: "1.0.2",
1291
1291
  description: "Command-line access to Trademark Turtle search, reports, and service status",
1292
1292
  repository: {
1293
1293
  type: "git",
@@ -1412,6 +1412,62 @@ class BadRequestError extends CliError {
1412
1412
  }
1413
1413
  }
1414
1414
 
1415
+ // src/prompt.ts
1416
+ function readHiddenApiKey(input = process.stdin, output = process.stderr) {
1417
+ if (!(input.isTTY && output.isTTY && input.setRawMode)) {
1418
+ return Promise.reject(new BadRequestError("Interactive input requires a terminal; use --stdin instead"));
1419
+ }
1420
+ const wasFlowing = input.readableFlowing === true;
1421
+ const wasRaw = input.isRaw === true;
1422
+ input.setRawMode(true);
1423
+ return new Promise((resolve, reject) => {
1424
+ const bytes = [];
1425
+ const finish = (result) => {
1426
+ input.removeListener("data", onData);
1427
+ input.removeListener("end", onEnd);
1428
+ input.removeListener("error", onError);
1429
+ if (!wasRaw) {
1430
+ input.setRawMode?.(false);
1431
+ }
1432
+ if (!wasFlowing) {
1433
+ input.pause();
1434
+ }
1435
+ output.write(`
1436
+ `);
1437
+ if (result.error) {
1438
+ reject(result.error);
1439
+ } else {
1440
+ resolve(result.value ?? "");
1441
+ }
1442
+ };
1443
+ const cancel = () => finish({ error: new BadRequestError("API key input cancelled") });
1444
+ const onData = (chunk) => {
1445
+ for (const byte of Buffer.from(chunk)) {
1446
+ if (byte === 3 || byte === 4) {
1447
+ cancel();
1448
+ return;
1449
+ }
1450
+ if (byte === 10 || byte === 13) {
1451
+ finish({ value: Buffer.from(bytes).toString("utf8") });
1452
+ return;
1453
+ }
1454
+ if (byte === 8 || byte === 127) {
1455
+ bytes.pop();
1456
+ continue;
1457
+ }
1458
+ bytes.push(byte);
1459
+ }
1460
+ };
1461
+ const onEnd = () => cancel();
1462
+ const onError = (error) => finish({ error });
1463
+ input.on("data", onData);
1464
+ input.once("end", onEnd);
1465
+ input.once("error", onError);
1466
+ input.resume();
1467
+ output.write("API key: ");
1468
+ });
1469
+ }
1470
+
1415
1471
  // ../../node_modules/.bun/commander@15.0.0/node_modules/commander/lib/error.js
1416
1472
  class CommanderError extends Error {
1417
1473
  constructor(exitCode, code, message) {
@@ -3582,8 +3638,8 @@ Examples:
3582
3638
  printf '%s' "shirt title" | tt match --stdin
3583
3639
  `);
3584
3640
  const auth = program2.command("auth").description("Manage the selected API credential");
3585
- auth.command("set").description("Store an API key read from stdin").requiredOption("--stdin", "Read the API key from stdin").action(() => {
3586
- command = { kind: "auth-set" };
3641
+ auth.command("set").description("Store an API key in macOS Keychain").option("--stdin", "Read the API key from stdin instead of prompting").action((options) => {
3642
+ command = { kind: "auth-set", readsStdin: options.stdin === true };
3587
3643
  });
3588
3644
  auth.command("status").description("Validate and describe the selected credential").action(() => {
3589
3645
  command = { kind: "auth-status" };
@@ -3734,7 +3790,7 @@ async function runCli(args, dependencies) {
3734
3790
  const invocation = parsed.command;
3735
3791
  if (invocation.kind === "auth-set") {
3736
3792
  const origin = configuredOrigin(dependencies, parsed.baseUrl);
3737
- const token = dependencies.stdin.trim();
3793
+ const token = (invocation.readsStdin ? dependencies.stdin : await dependencies.promptSecret()).trim();
3738
3794
  if (!tokenPattern.test(token)) {
3739
3795
  throw new BadRequestError("Invalid Trademark Turtle API key");
3740
3796
  }
@@ -3803,6 +3859,7 @@ try {
3803
3859
  createClient: createTmturtleClient,
3804
3860
  env: process.env,
3805
3861
  keychain: createMacOsKeychain(),
3862
+ promptSecret: readHiddenApiKey,
3806
3863
  stdin: args.includes("--stdin") ? await new Response(Bun.stdin.stream()).text() : "",
3807
3864
  version: package_default.version
3808
3865
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmturtle/cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Command-line access to Trademark Turtle search, reports, and service status",
5
5
  "repository": {
6
6
  "type": "git",