@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.
- package/dist/index.js +116 -31
- 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
|
-
|
|
11
|
-
|
|
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
|
|
47
|
-
|
|
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(
|
|
52
|
-
mkdirSync2(
|
|
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.
|
|
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:
|
|
470
|
+
const { readFileSync: readFileSync5 } = await import("fs");
|
|
460
471
|
const { basename, extname } = await import("path");
|
|
461
|
-
const buffer =
|
|
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.
|
|
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(
|
|
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(
|
|
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:
|
|
1675
|
+
const { existsSync: existsSync6 } = await import("fs");
|
|
1665
1676
|
const { resolve } = await import("path");
|
|
1666
1677
|
const filePath = resolve(opts.file);
|
|
1667
|
-
if (!
|
|
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 {
|
|
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 =
|
|
2137
|
-
const tokensPath =
|
|
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) ?
|
|
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.
|
|
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
|
|
2453
|
-
import { homedir as
|
|
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
|
|
2470
|
+
return join6(homedir2(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
2461
2471
|
}
|
|
2462
|
-
return
|
|
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
|
|
2480
|
+
return join6(process.cwd(), ".mcp.json");
|
|
2471
2481
|
}
|
|
2472
|
-
return
|
|
2482
|
+
return join6(homedir2(), ".claude", "settings.json");
|
|
2473
2483
|
}
|
|
2474
2484
|
},
|
|
2475
2485
|
cursor: {
|
|
2476
2486
|
name: "Cursor",
|
|
2477
2487
|
supportsScope: false,
|
|
2478
|
-
configPath: () =>
|
|
2488
|
+
configPath: () => join6(process.cwd(), ".cursor", "mcp.json")
|
|
2479
2489
|
},
|
|
2480
2490
|
windsurf: {
|
|
2481
2491
|
name: "Windsurf",
|
|
2482
2492
|
supportsScope: false,
|
|
2483
|
-
configPath: () =>
|
|
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.
|
|
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();
|