@costlens/mcp-server 0.5.0 → 0.6.0
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/cli.js +143 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -398,6 +398,11 @@ var CONFIG_FILE = (0, import_path2.join)(CONFIG_DIR, "config.json");
|
|
|
398
398
|
var API_BASE2 = process.env.COSTLENS_API_URL || "https://api.costlens.dev";
|
|
399
399
|
var APP_URL = process.env.COSTLENS_APP_URL || "https://costlens.dev";
|
|
400
400
|
function readConfig() {
|
|
401
|
+
try {
|
|
402
|
+
const projectConfig = JSON.parse((0, import_fs2.readFileSync)((0, import_path2.join)(process.cwd(), ".costlens.json"), "utf-8"));
|
|
403
|
+
if (projectConfig.key) return { apiKey: projectConfig.key };
|
|
404
|
+
} catch {
|
|
405
|
+
}
|
|
401
406
|
try {
|
|
402
407
|
return JSON.parse((0, import_fs2.readFileSync)(CONFIG_FILE, "utf-8"));
|
|
403
408
|
} catch {
|
|
@@ -646,7 +651,8 @@ else if (command === "setup") setup();
|
|
|
646
651
|
else if (command === "hooks") {
|
|
647
652
|
installHooks();
|
|
648
653
|
process.exit(0);
|
|
649
|
-
} else
|
|
654
|
+
} else if (command === "doctor") doctor();
|
|
655
|
+
else {
|
|
650
656
|
init_index();
|
|
651
657
|
}
|
|
652
658
|
async function setup() {
|
|
@@ -659,3 +665,139 @@ async function setup() {
|
|
|
659
665
|
}
|
|
660
666
|
await login();
|
|
661
667
|
}
|
|
668
|
+
async function doctor() {
|
|
669
|
+
console.log("\n CostLens Doctor\n");
|
|
670
|
+
const config = readConfig();
|
|
671
|
+
const issues = [];
|
|
672
|
+
let keyValid = false;
|
|
673
|
+
let planData = null;
|
|
674
|
+
let spendData = null;
|
|
675
|
+
if (!config.apiKey) {
|
|
676
|
+
console.log(" Key: not configured");
|
|
677
|
+
issues.push("No API key. Run: npx @costlens/mcp-server setup");
|
|
678
|
+
} else {
|
|
679
|
+
try {
|
|
680
|
+
const res = await fetch(`${API_BASE2}/v1/plan`, {
|
|
681
|
+
headers: { Authorization: `Bearer ${config.apiKey}` },
|
|
682
|
+
signal: AbortSignal.timeout(5e3)
|
|
683
|
+
});
|
|
684
|
+
if (res.ok) {
|
|
685
|
+
planData = await res.json();
|
|
686
|
+
keyValid = true;
|
|
687
|
+
console.log(` Key: valid (${config.apiKey.slice(0, 10)}...)`);
|
|
688
|
+
} else {
|
|
689
|
+
console.log(` Key: invalid (${res.status})`);
|
|
690
|
+
issues.push("API key is invalid or expired. Run: npx @costlens/mcp-server login");
|
|
691
|
+
}
|
|
692
|
+
} catch (e) {
|
|
693
|
+
console.log(` Key: error (${e.message})`);
|
|
694
|
+
issues.push("Cannot reach CostLens API. Check your network.");
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
if (planData) {
|
|
698
|
+
console.log(` Plan: ${planData.plan}`);
|
|
699
|
+
console.log(` Tracking: ${planData.keySettings?.trackingMode || "manual"}`);
|
|
700
|
+
}
|
|
701
|
+
if (keyValid) {
|
|
702
|
+
try {
|
|
703
|
+
const res = await fetch(`${API_BASE2}/v1/spend`, {
|
|
704
|
+
headers: { Authorization: `Bearer ${config.apiKey}` },
|
|
705
|
+
signal: AbortSignal.timeout(5e3)
|
|
706
|
+
});
|
|
707
|
+
if (res.ok) {
|
|
708
|
+
spendData = await res.json();
|
|
709
|
+
const daily = spendData.daily?.cost ?? 0;
|
|
710
|
+
const budget = spendData.dailyBudget;
|
|
711
|
+
if (budget) {
|
|
712
|
+
console.log(` Budget: $${daily.toFixed(2)} / $${budget.toFixed(2)} today`);
|
|
713
|
+
} else {
|
|
714
|
+
console.log(` Spend: $${daily.toFixed(4)} today`);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
} catch {
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
const kiroPath = (0, import_path2.join)((0, import_os2.homedir)(), ".kiro", "settings", "mcp.json");
|
|
721
|
+
const cursorPath = (0, import_path2.join)((0, import_os2.homedir)(), ".cursor", "mcp.json");
|
|
722
|
+
const claudePath = (0, import_path2.join)((0, import_os2.homedir)(), ".claude", "mcp_servers.json");
|
|
723
|
+
const agents = [
|
|
724
|
+
{ name: "Kiro", path: kiroPath },
|
|
725
|
+
{ name: "Cursor", path: cursorPath },
|
|
726
|
+
{ name: "Claude Code", path: claudePath }
|
|
727
|
+
];
|
|
728
|
+
let agentConfigured = false;
|
|
729
|
+
for (const { name, path } of agents) {
|
|
730
|
+
if ((0, import_fs2.existsSync)(path)) {
|
|
731
|
+
try {
|
|
732
|
+
const content = JSON.parse((0, import_fs2.readFileSync)(path, "utf-8"));
|
|
733
|
+
if (content.mcpServers?.costlens) {
|
|
734
|
+
console.log(` MCP: ${name} (configured)`);
|
|
735
|
+
agentConfigured = true;
|
|
736
|
+
break;
|
|
737
|
+
}
|
|
738
|
+
} catch {
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
if (!agentConfigured) {
|
|
743
|
+
console.log(" MCP: not configured");
|
|
744
|
+
issues.push("No agent configured. Run: npx @costlens/mcp-server setup");
|
|
745
|
+
}
|
|
746
|
+
const cwd = process.cwd();
|
|
747
|
+
const hookPath = (0, import_path2.join)(cwd, ".git", "hooks", "post-commit");
|
|
748
|
+
if ((0, import_fs2.existsSync)(hookPath)) {
|
|
749
|
+
try {
|
|
750
|
+
const content = (0, import_fs2.readFileSync)(hookPath, "utf-8");
|
|
751
|
+
if (content.includes(HOOK_MARKER)) {
|
|
752
|
+
console.log(" Git hook: installed (post-commit)");
|
|
753
|
+
} else {
|
|
754
|
+
console.log(" Git hook: exists (not CostLens)");
|
|
755
|
+
}
|
|
756
|
+
} catch {
|
|
757
|
+
console.log(" Git hook: error reading");
|
|
758
|
+
}
|
|
759
|
+
} else if ((0, import_fs2.existsSync)((0, import_path2.join)(cwd, ".git"))) {
|
|
760
|
+
console.log(" Git hook: not installed");
|
|
761
|
+
issues.push("Git hook not installed. Run: npx @costlens/mcp-server hooks");
|
|
762
|
+
} else {
|
|
763
|
+
console.log(" Git hook: no git repo in cwd");
|
|
764
|
+
}
|
|
765
|
+
const pkgPath = (0, import_path2.join)(cwd, "package.json");
|
|
766
|
+
if ((0, import_fs2.existsSync)(pkgPath)) {
|
|
767
|
+
try {
|
|
768
|
+
const pkg = JSON.parse((0, import_fs2.readFileSync)(pkgPath, "utf-8"));
|
|
769
|
+
const sdkVersion = pkg.dependencies?.costlens || pkg.devDependencies?.costlens;
|
|
770
|
+
if (sdkVersion) {
|
|
771
|
+
console.log(` SDK: installed (${sdkVersion})`);
|
|
772
|
+
} else {
|
|
773
|
+
console.log(" SDK: not installed");
|
|
774
|
+
}
|
|
775
|
+
} catch {
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
if (keyValid) {
|
|
779
|
+
try {
|
|
780
|
+
const res = await fetch(`${API_BASE2}/v1/productivity/github?period=week`, {
|
|
781
|
+
headers: { Authorization: `Bearer ${config.apiKey}` },
|
|
782
|
+
signal: AbortSignal.timeout(5e3)
|
|
783
|
+
});
|
|
784
|
+
if (res.ok) {
|
|
785
|
+
console.log(" GitHub: connected");
|
|
786
|
+
} else if (res.status === 404) {
|
|
787
|
+
console.log(" GitHub: not connected");
|
|
788
|
+
}
|
|
789
|
+
} catch {
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
console.log("");
|
|
793
|
+
if (issues.length === 0) {
|
|
794
|
+
console.log(" All good.\n");
|
|
795
|
+
} else {
|
|
796
|
+
console.log(" Issues:\n");
|
|
797
|
+
for (const issue of issues) {
|
|
798
|
+
console.log(` - ${issue}`);
|
|
799
|
+
}
|
|
800
|
+
console.log("");
|
|
801
|
+
}
|
|
802
|
+
process.exit(issues.length > 0 ? 1 : 0);
|
|
803
|
+
}
|