@betterness/cli 1.3.1 → 1.3.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.
Files changed (2) hide show
  1. package/dist/index.js +116 -31
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3,12 +3,25 @@
3
3
  // src/program.ts
4
4
  import { Command } from "commander";
5
5
 
6
+ // src/commands/auth.ts
7
+ import { join as join4 } from "path";
8
+
6
9
  // src/auth/credentialStore.ts
7
10
  import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from "fs";
11
+ import { join as join2 } from "path";
12
+
13
+ // src/paths.ts
8
14
  import { join } from "path";
9
15
  import { homedir } from "os";
10
- var CONFIG_DIR = join(homedir(), ".betterness");
11
- var CREDENTIALS_FILE = join(CONFIG_DIR, "credentials.json");
16
+ function resolveConfigDir() {
17
+ const apiUrl = "https://api.betterness.ai";
18
+ const isDev = apiUrl.includes("localhost") || apiUrl.includes("127.0.0.1");
19
+ return join(homedir(), isDev ? ".betterness-dev" : ".betterness");
20
+ }
21
+ var CONFIG_DIR = resolveConfigDir();
22
+
23
+ // src/auth/credentialStore.ts
24
+ var CREDENTIALS_FILE = join2(CONFIG_DIR, "credentials.json");
12
25
  function ensureConfigDir() {
13
26
  if (!existsSync(CONFIG_DIR)) {
14
27
  mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
@@ -43,13 +56,11 @@ function deleteCredentials() {
43
56
 
44
57
  // src/auth/tokenStore.ts
45
58
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2, unlinkSync as unlinkSync2 } from "fs";
46
- import { join as join2 } from "path";
47
- import { homedir as homedir2 } from "os";
48
- var CONFIG_DIR2 = join2(homedir2(), ".betterness");
49
- var TOKENS_FILE = join2(CONFIG_DIR2, "tokens.json");
59
+ import { join as join3 } from "path";
60
+ var TOKENS_FILE = join3(CONFIG_DIR, "tokens.json");
50
61
  function ensureConfigDir2() {
51
- if (!existsSync2(CONFIG_DIR2)) {
52
- mkdirSync2(CONFIG_DIR2, { recursive: true, mode: 448 });
62
+ if (!existsSync2(CONFIG_DIR)) {
63
+ mkdirSync2(CONFIG_DIR, { recursive: true, mode: 448 });
53
64
  }
54
65
  }
55
66
  function loadTokens() {
@@ -408,7 +419,7 @@ var ApiClient = class {
408
419
  headers: {
409
420
  "Authorization": `Bearer ${this.apiKey}`,
410
421
  "Content-Type": "application/json",
411
- "User-Agent": `betterness-cli/${"1.3.1"}`,
422
+ "User-Agent": `betterness-cli/${"1.3.2"}`,
412
423
  "Accept": "application/json"
413
424
  },
414
425
  body: body ? JSON.stringify(body) : void 0,
@@ -456,9 +467,9 @@ var ApiClient = class {
456
467
  return this.unwrap(raw, schema);
457
468
  }
458
469
  async upload(path, filePath, fieldName = "file", schema) {
459
- const { readFileSync: readFileSync4 } = await import("fs");
470
+ const { readFileSync: readFileSync5 } = await import("fs");
460
471
  const { basename, extname } = await import("path");
461
- const buffer = readFileSync4(filePath);
472
+ const buffer = readFileSync5(filePath);
462
473
  const fileName = basename(filePath);
463
474
  const ext = extname(filePath).toLowerCase();
464
475
  const mimeType = ext === ".pdf" ? "application/pdf" : "application/octet-stream";
@@ -472,7 +483,7 @@ var ApiClient = class {
472
483
  method: "POST",
473
484
  headers: {
474
485
  "Authorization": `Bearer ${this.apiKey}`,
475
- "User-Agent": `betterness-cli/${"1.3.1"}`,
486
+ "User-Agent": `betterness-cli/${"1.3.2"}`,
476
487
  "Accept": "application/json"
477
488
  },
478
489
  body: formData,
@@ -892,7 +903,7 @@ async function loginWithApiKey(apiKey) {
892
903
  name: user.firstName ?? void 0
893
904
  });
894
905
  console.log(`Logged in as: ${user.firstName ?? "Unknown"} (${user.email ?? "no email"})`);
895
- console.log("Credentials saved to ~/.betterness/credentials.json");
906
+ console.log(`Credentials saved to ${join4(CONFIG_DIR, "credentials.json")}`);
896
907
  }
897
908
  async function loginWithOAuth() {
898
909
  const result = await startOAuthLogin();
@@ -922,7 +933,7 @@ async function loginWithOAuth() {
922
933
  name: user.firstName ?? result.name
923
934
  });
924
935
  console.log(`Logged in as: ${user.firstName ?? "Unknown"} (${user.email ?? "no email"})`);
925
- console.log("Credentials saved to ~/.betterness/tokens.json");
936
+ console.log(`Credentials saved to ${join4(CONFIG_DIR, "tokens.json")}`);
926
937
  }
927
938
 
928
939
  // src/commands/profile.ts
@@ -1661,10 +1672,10 @@ function registerLabResultsCommands(program2) {
1661
1672
  });
1662
1673
  labResults.command("upload").description("Upload a lab result PDF for processing").requiredOption("--file <path>", "Path to the PDF file").action(async (opts, cmd) => {
1663
1674
  try {
1664
- const { existsSync: existsSync5 } = await import("fs");
1675
+ const { existsSync: existsSync6 } = await import("fs");
1665
1676
  const { resolve } = await import("path");
1666
1677
  const filePath = resolve(opts.file);
1667
- if (!existsSync5(filePath)) {
1678
+ if (!existsSync6(filePath)) {
1668
1679
  console.error(`File not found: ${filePath}`);
1669
1680
  process.exit(1);
1670
1681
  }
@@ -2127,21 +2138,20 @@ function registerSchemaCommand(program2) {
2127
2138
  }
2128
2139
 
2129
2140
  // src/commands/debug.ts
2130
- import { homedir as homedir3 } from "os";
2131
- import { join as join3 } from "path";
2141
+ import { join as join5 } from "path";
2132
2142
  import { existsSync as existsSync3 } from "fs";
2133
2143
  function registerDebugCommands(program2) {
2134
2144
  const debug = program2.command("debug", { hidden: true }).description("Internal debug utilities");
2135
2145
  debug.command("config").description("Show resolved configuration").action((_, cmd) => {
2136
- const credentialsPath = join3(homedir3(), ".betterness", "credentials.json");
2137
- const tokensPath = join3(homedir3(), ".betterness", "tokens.json");
2146
+ const credentialsPath = join5(CONFIG_DIR, "credentials.json");
2147
+ const tokensPath = join5(CONFIG_DIR, "tokens.json");
2138
2148
  const stored = loadCredentials();
2139
2149
  const tokens = loadTokens();
2140
2150
  const envKey = process.env.BETTERNESS_API_KEY;
2141
2151
  const parentOpts = cmd.optsWithGlobals();
2142
- const authSource = parentOpts.apiKey ? "--api-key flag" : envKey ? "BETTERNESS_API_KEY env" : tokens && !isTokenExpired(tokens) ? "OAuth tokens (~/.betterness/tokens.json)" : stored ? "API key (~/.betterness/credentials.json)" : "none";
2152
+ const authSource = parentOpts.apiKey ? "--api-key flag" : envKey ? "BETTERNESS_API_KEY env" : tokens && !isTokenExpired(tokens) ? `OAuth tokens (${tokensPath})` : stored ? `API key (${credentialsPath})` : "none";
2143
2153
  const config = {
2144
- version: "1.3.1",
2154
+ version: "1.3.2",
2145
2155
  apiUrl: "https://api.betterness.ai",
2146
2156
  auth0Domain: "betterness.us.auth0.com",
2147
2157
  auth0ClientId: "g4lqYHRQb2QMgdRKIlKwoTJl6eu41pWn",
@@ -2449,17 +2459,17 @@ function formatAnswerValue(value) {
2449
2459
 
2450
2460
  // src/commands/mcp.ts
2451
2461
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3, copyFileSync } from "fs";
2452
- import { join as join4, dirname } from "path";
2453
- import { homedir as homedir4, platform } from "os";
2462
+ import { join as join6, dirname } from "path";
2463
+ import { homedir as homedir2, platform } from "os";
2454
2464
  var CLIENTS = {
2455
2465
  claude: {
2456
2466
  name: "Claude Desktop",
2457
2467
  supportsScope: false,
2458
2468
  configPath: () => {
2459
2469
  if (platform() === "darwin") {
2460
- return join4(homedir4(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
2470
+ return join6(homedir2(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
2461
2471
  }
2462
- return join4(homedir4(), ".config", "Claude", "claude_desktop_config.json");
2472
+ return join6(homedir2(), ".config", "Claude", "claude_desktop_config.json");
2463
2473
  }
2464
2474
  },
2465
2475
  "claude-code": {
@@ -2467,20 +2477,20 @@ var CLIENTS = {
2467
2477
  supportsScope: true,
2468
2478
  configPath: (scope) => {
2469
2479
  if (scope === "project") {
2470
- return join4(process.cwd(), ".mcp.json");
2480
+ return join6(process.cwd(), ".mcp.json");
2471
2481
  }
2472
- return join4(homedir4(), ".claude", "settings.json");
2482
+ return join6(homedir2(), ".claude", "settings.json");
2473
2483
  }
2474
2484
  },
2475
2485
  cursor: {
2476
2486
  name: "Cursor",
2477
2487
  supportsScope: false,
2478
- configPath: () => join4(process.cwd(), ".cursor", "mcp.json")
2488
+ configPath: () => join6(process.cwd(), ".cursor", "mcp.json")
2479
2489
  },
2480
2490
  windsurf: {
2481
2491
  name: "Windsurf",
2482
2492
  supportsScope: false,
2483
- configPath: () => join4(homedir4(), ".codeium", "windsurf", "mcp_config.json")
2493
+ configPath: () => join6(homedir2(), ".codeium", "windsurf", "mcp_config.json")
2484
2494
  }
2485
2495
  };
2486
2496
  var VALID_CLIENTS = Object.keys(CLIENTS).join(", ");
@@ -2670,7 +2680,7 @@ Restart ${client.name} to activate. Then try asking:
2670
2680
  // src/program.ts
2671
2681
  function createProgram() {
2672
2682
  const program2 = new Command();
2673
- program2.name("betterness").description("Betterness CLI - Agent-first terminal interface for the Betterness platform").version("1.3.1").option("--api-key <key>", "API key (overrides env and stored credentials)").option("--json", "Output as JSON").option("--markdown", "Output as Markdown").option("--quiet", "Suppress output (exit code only)");
2683
+ program2.name("betterness").description("Betterness CLI - Agent-first terminal interface for the Betterness platform").version("1.3.2").option("--api-key <key>", "API key (overrides env and stored credentials)").option("--json", "Output as JSON").option("--markdown", "Output as Markdown").option("--quiet", "Suppress output (exit code only)");
2674
2684
  registerAuthCommands(program2);
2675
2685
  registerProfileCommands(program2);
2676
2686
  registerBiomarkersCommands(program2);
@@ -2694,6 +2704,81 @@ function createProgram() {
2694
2704
  return program2;
2695
2705
  }
2696
2706
 
2707
+ // src/updateCheck.ts
2708
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
2709
+ import { join as join7 } from "path";
2710
+ var CACHE_FILE = join7(CONFIG_DIR, "update-check.json");
2711
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
2712
+ var REGISTRY_TIMEOUT_MS = 3e3;
2713
+ function loadCache() {
2714
+ if (!existsSync5(CACHE_FILE)) return null;
2715
+ try {
2716
+ return JSON.parse(readFileSync4(CACHE_FILE, "utf-8"));
2717
+ } catch {
2718
+ return null;
2719
+ }
2720
+ }
2721
+ function saveCache(cache) {
2722
+ if (!existsSync5(CONFIG_DIR)) {
2723
+ mkdirSync4(CONFIG_DIR, { recursive: true, mode: 448 });
2724
+ }
2725
+ writeFileSync4(CACHE_FILE, JSON.stringify(cache), { mode: 384 });
2726
+ }
2727
+ function isNewer(latest, current) {
2728
+ const parse = (v) => v.replace(/^v/, "").split(".").map(Number);
2729
+ const [lMaj, lMin, lPat] = parse(latest);
2730
+ const [cMaj, cMin, cPat] = parse(current);
2731
+ if (lMaj !== cMaj) return lMaj > cMaj;
2732
+ if (lMin !== cMin) return lMin > cMin;
2733
+ return lPat > cPat;
2734
+ }
2735
+ async function fetchLatestVersion() {
2736
+ const controller = new AbortController();
2737
+ const timeoutId = setTimeout(() => controller.abort(), REGISTRY_TIMEOUT_MS);
2738
+ try {
2739
+ const res = await fetch("https://registry.npmjs.org/@betterness/cli/latest", {
2740
+ headers: { Accept: "application/json" },
2741
+ signal: controller.signal
2742
+ });
2743
+ clearTimeout(timeoutId);
2744
+ if (!res.ok) return null;
2745
+ const data = await res.json();
2746
+ return data.version ?? null;
2747
+ } catch {
2748
+ clearTimeout(timeoutId);
2749
+ return null;
2750
+ }
2751
+ }
2752
+ function scheduleUpdateCheck() {
2753
+ const currentVersion = "1.3.2";
2754
+ if (currentVersion === "0.0.0") return;
2755
+ const cache = loadCache();
2756
+ if (cache && Date.now() - cache.lastCheck < CHECK_INTERVAL_MS) {
2757
+ if (isNewer(cache.latestVersion, currentVersion)) {
2758
+ printNotice(currentVersion, cache.latestVersion);
2759
+ }
2760
+ return;
2761
+ }
2762
+ fetchLatestVersion().then((latest) => {
2763
+ if (!latest) return;
2764
+ saveCache({ lastCheck: Date.now(), latestVersion: latest });
2765
+ if (isNewer(latest, currentVersion)) {
2766
+ printNotice(currentVersion, latest);
2767
+ }
2768
+ }).catch(() => {
2769
+ });
2770
+ }
2771
+ function printNotice(current, latest) {
2772
+ process.stderr.write(
2773
+ `
2774
+ Update available: ${current} \u2192 ${latest}
2775
+ Run \`npm i -g @betterness/cli\` to update.
2776
+
2777
+ `
2778
+ );
2779
+ }
2780
+
2697
2781
  // src/index.ts
2782
+ scheduleUpdateCheck();
2698
2783
  var program = createProgram();
2699
2784
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@betterness/cli",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "description": "Betterness CLI - Agent-first terminal interface for the Betterness platform",
5
5
  "type": "module",
6
6
  "bin": {