garmin-bud 0.2.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.
Files changed (146) hide show
  1. package/.env.example +11 -0
  2. package/CHANGELOG.md +56 -0
  3. package/LICENSE +21 -0
  4. package/QUICKSTART.md +174 -0
  5. package/README.md +227 -0
  6. package/dist/appDb.d.ts +32 -0
  7. package/dist/appDb.d.ts.map +1 -0
  8. package/dist/appDb.js +131 -0
  9. package/dist/appDb.js.map +1 -0
  10. package/dist/check.d.ts +9 -0
  11. package/dist/check.d.ts.map +1 -0
  12. package/dist/check.js +156 -0
  13. package/dist/check.js.map +1 -0
  14. package/dist/cli.d.ts +3 -0
  15. package/dist/cli.d.ts.map +1 -0
  16. package/dist/cli.js +187 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/config.d.ts +28 -0
  19. package/dist/config.d.ts.map +1 -0
  20. package/dist/config.js +108 -0
  21. package/dist/config.js.map +1 -0
  22. package/dist/dashboard.d.ts +4 -0
  23. package/dist/dashboard.d.ts.map +1 -0
  24. package/dist/dashboard.js +111 -0
  25. package/dist/dashboard.js.map +1 -0
  26. package/dist/garmin/auth.d.ts +10 -0
  27. package/dist/garmin/auth.d.ts.map +1 -0
  28. package/dist/garmin/auth.js +81 -0
  29. package/dist/garmin/auth.js.map +1 -0
  30. package/dist/garmin/cache.d.ts +20 -0
  31. package/dist/garmin/cache.d.ts.map +1 -0
  32. package/dist/garmin/cache.js +115 -0
  33. package/dist/garmin/cache.js.map +1 -0
  34. package/dist/garmin/client.d.ts +5 -0
  35. package/dist/garmin/client.d.ts.map +1 -0
  36. package/dist/garmin/client.js +68 -0
  37. package/dist/garmin/client.js.map +1 -0
  38. package/dist/garmin/garminApiTypes.d.ts +70 -0
  39. package/dist/garmin/garminApiTypes.d.ts.map +1 -0
  40. package/dist/garmin/garminApiTypes.js +3 -0
  41. package/dist/garmin/garminApiTypes.js.map +1 -0
  42. package/dist/garmin/garminConnect.d.ts +17 -0
  43. package/dist/garmin/garminConnect.d.ts.map +1 -0
  44. package/dist/garmin/garminConnect.js +5 -0
  45. package/dist/garmin/garminConnect.js.map +1 -0
  46. package/dist/garmin/rawApi.d.ts +18 -0
  47. package/dist/garmin/rawApi.d.ts.map +1 -0
  48. package/dist/garmin/rawApi.js +53 -0
  49. package/dist/garmin/rawApi.js.map +1 -0
  50. package/dist/garmin/types.d.ts +68 -0
  51. package/dist/garmin/types.d.ts.map +1 -0
  52. package/dist/garmin/types.js +11 -0
  53. package/dist/garmin/types.js.map +1 -0
  54. package/dist/httpServer.d.ts +7 -0
  55. package/dist/httpServer.d.ts.map +1 -0
  56. package/dist/httpServer.js +386 -0
  57. package/dist/httpServer.js.map +1 -0
  58. package/dist/index.d.ts +3 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +11 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/mcpConfig.d.ts +37 -0
  63. package/dist/mcpConfig.d.ts.map +1 -0
  64. package/dist/mcpConfig.js +83 -0
  65. package/dist/mcpConfig.js.map +1 -0
  66. package/dist/pairApi.d.ts +14 -0
  67. package/dist/pairApi.d.ts.map +1 -0
  68. package/dist/pairApi.js +34 -0
  69. package/dist/pairApi.js.map +1 -0
  70. package/dist/promptApi.d.ts +13 -0
  71. package/dist/promptApi.d.ts.map +1 -0
  72. package/dist/promptApi.js +102 -0
  73. package/dist/promptApi.js.map +1 -0
  74. package/dist/server.d.ts +10 -0
  75. package/dist/server.d.ts.map +1 -0
  76. package/dist/server.js +67 -0
  77. package/dist/server.js.map +1 -0
  78. package/dist/setup.d.ts +2 -0
  79. package/dist/setup.d.ts.map +1 -0
  80. package/dist/setup.js +194 -0
  81. package/dist/setup.js.map +1 -0
  82. package/dist/toolErrors.d.ts +2 -0
  83. package/dist/toolErrors.d.ts.map +1 -0
  84. package/dist/toolErrors.js +18 -0
  85. package/dist/toolErrors.js.map +1 -0
  86. package/dist/tools/activities.d.ts +11 -0
  87. package/dist/tools/activities.d.ts.map +1 -0
  88. package/dist/tools/activities.js +134 -0
  89. package/dist/tools/activities.js.map +1 -0
  90. package/dist/tools/bodyComposition.d.ts +7 -0
  91. package/dist/tools/bodyComposition.d.ts.map +1 -0
  92. package/dist/tools/bodyComposition.js +97 -0
  93. package/dist/tools/bodyComposition.js.map +1 -0
  94. package/dist/tools/heartRate.d.ts +7 -0
  95. package/dist/tools/heartRate.d.ts.map +1 -0
  96. package/dist/tools/heartRate.js +88 -0
  97. package/dist/tools/heartRate.js.map +1 -0
  98. package/dist/tools/index.d.ts +39 -0
  99. package/dist/tools/index.d.ts.map +1 -0
  100. package/dist/tools/index.js +77 -0
  101. package/dist/tools/index.js.map +1 -0
  102. package/dist/tools/recovery.d.ts +16 -0
  103. package/dist/tools/recovery.d.ts.map +1 -0
  104. package/dist/tools/recovery.js +214 -0
  105. package/dist/tools/recovery.js.map +1 -0
  106. package/dist/tools/sleep.d.ts +7 -0
  107. package/dist/tools/sleep.d.ts.map +1 -0
  108. package/dist/tools/sleep.js +93 -0
  109. package/dist/tools/sleep.js.map +1 -0
  110. package/dist/tools/stress.d.ts +7 -0
  111. package/dist/tools/stress.d.ts.map +1 -0
  112. package/dist/tools/stress.js +69 -0
  113. package/dist/tools/stress.js.map +1 -0
  114. package/dist/tools/trainingInsights.d.ts +7 -0
  115. package/dist/tools/trainingInsights.d.ts.map +1 -0
  116. package/dist/tools/trainingInsights.js +61 -0
  117. package/dist/tools/trainingInsights.js.map +1 -0
  118. package/dist/tools/types.d.ts +12 -0
  119. package/dist/tools/types.d.ts.map +1 -0
  120. package/dist/tools/types.js +2 -0
  121. package/dist/tools/types.js.map +1 -0
  122. package/dist/tools/vo2Max.d.ts +7 -0
  123. package/dist/tools/vo2Max.d.ts.map +1 -0
  124. package/dist/tools/vo2Max.js +72 -0
  125. package/dist/tools/vo2Max.js.map +1 -0
  126. package/dist/utils/batch.d.ts +2 -0
  127. package/dist/utils/batch.d.ts.map +1 -0
  128. package/dist/utils/batch.js +18 -0
  129. package/dist/utils/batch.js.map +1 -0
  130. package/dist/utils/helpers.d.ts +19 -0
  131. package/dist/utils/helpers.d.ts.map +1 -0
  132. package/dist/utils/helpers.js +131 -0
  133. package/dist/utils/helpers.js.map +1 -0
  134. package/dist/utils/logger.d.ts +4 -0
  135. package/dist/utils/logger.d.ts.map +1 -0
  136. package/dist/utils/logger.js +59 -0
  137. package/dist/utils/logger.js.map +1 -0
  138. package/dist/version.d.ts +2 -0
  139. package/dist/version.d.ts.map +1 -0
  140. package/dist/version.js +7 -0
  141. package/dist/version.js.map +1 -0
  142. package/dist/watchApi.d.ts +47 -0
  143. package/dist/watchApi.d.ts.map +1 -0
  144. package/dist/watchApi.js +222 -0
  145. package/dist/watchApi.js.map +1 -0
  146. package/package.json +77 -0
@@ -0,0 +1,102 @@
1
+ import Anthropic from "@anthropic-ai/sdk";
2
+ import { randomBytes } from "node:crypto";
3
+ import { createPromptJob, getPromptJob, updatePromptJob } from "./appDb.js";
4
+ import { appConfig } from "./config.js";
5
+ import { buildWatchSummary } from "./watchApi.js";
6
+ import { logger } from "./utils/logger.js";
7
+ // SECTION: Prompt API — Claude integration
8
+ const MODEL = "claude-haiku-4-5-20251001";
9
+ const MAX_TOKENS = 300;
10
+ function buildJobId() {
11
+ return randomBytes(12).toString("hex");
12
+ }
13
+ function formatHealthContext(summary) {
14
+ const lines = ["Current health snapshot:"];
15
+ if (summary.recovery) {
16
+ lines.push(`- Recovery: ${summary.recovery.score}/100 (${summary.recovery.label})`);
17
+ }
18
+ if (summary.sleep) {
19
+ lines.push(`- Sleep last night: ${summary.sleep.hours}h${summary.sleep.score ? `, score ${summary.sleep.score}` : ""} (${summary.sleep.label})`);
20
+ }
21
+ if (summary.stress) {
22
+ lines.push(`- Avg stress (7d): ${summary.stress.avg} (${summary.stress.label})`);
23
+ }
24
+ if (summary.vo2max) {
25
+ lines.push(`- VO2 Max: ${summary.vo2max.value} (${summary.vo2max.trend})`);
26
+ }
27
+ if (summary.heart_rate) {
28
+ lines.push(`- Resting HR: ${summary.heart_rate.resting} bpm`);
29
+ }
30
+ if (summary.activity) {
31
+ lines.push(`- Last activity: ${summary.activity.name}${summary.activity.duration_min ? `, ${summary.activity.duration_min}min` : ""}${summary.activity.distance_km ? `, ${summary.activity.distance_km}km` : ""}`);
32
+ }
33
+ return lines.join("\n");
34
+ }
35
+ async function callClaude(prompt, healthContext) {
36
+ const apiKey = appConfig.anthropicApiKey;
37
+ if (!apiKey) {
38
+ throw new Error("ANTHROPIC_API_KEY not configured. Set it in the dashboard or .env file.");
39
+ }
40
+ const client = new Anthropic({ apiKey });
41
+ const message = await client.messages.create({
42
+ model: MODEL,
43
+ max_tokens: MAX_TOKENS,
44
+ system: `You are a concise fitness coach assistant integrated into a Garmin smartwatch.
45
+ Answer in 2-3 short sentences maximum. Be direct and actionable. No markdown formatting.
46
+ ${healthContext}`,
47
+ messages: [{ role: "user", content: prompt }],
48
+ });
49
+ const block = message.content[0];
50
+ if (block?.type !== "text") {
51
+ throw new Error("Unexpected response type from Claude");
52
+ }
53
+ return block.text.trim();
54
+ }
55
+ export function submitPrompt(prompt) {
56
+ const id = buildJobId();
57
+ createPromptJob(id, prompt);
58
+ // Fire-and-forget — process asynchronously
59
+ processPromptJob(id, prompt).catch((err) => {
60
+ logger.error({ err, id }, "Prompt job processing failed unexpectedly");
61
+ });
62
+ return { job_id: id };
63
+ }
64
+ async function processPromptJob(id, prompt) {
65
+ updatePromptJob(id, { status: "running" });
66
+ try {
67
+ const summary = await buildWatchSummary();
68
+ const healthContext = formatHealthContext(summary);
69
+ const result = await callClaude(prompt, healthContext);
70
+ updatePromptJob(id, { status: "done", result });
71
+ }
72
+ catch (err) {
73
+ const message = err instanceof Error ? err.message : String(err);
74
+ logger.error({ err, id }, "Prompt job failed");
75
+ updatePromptJob(id, { status: "error", error: message });
76
+ }
77
+ }
78
+ export function getPromptStatus(id) {
79
+ const job = getPromptJob(id);
80
+ if (!job)
81
+ return null;
82
+ return {
83
+ status: job.status,
84
+ result: job.result ?? undefined,
85
+ error: job.error ?? undefined,
86
+ };
87
+ }
88
+ export async function generateDailyInsight(summary) {
89
+ const apiKey = appConfig.anthropicApiKey;
90
+ if (!apiKey)
91
+ return null;
92
+ try {
93
+ const healthContext = formatHealthContext(summary);
94
+ const result = await callClaude("Give me one sentence of actionable advice for today based on my health data.", healthContext);
95
+ return result;
96
+ }
97
+ catch (err) {
98
+ logger.warn({ err }, "Daily insight generation failed");
99
+ return null;
100
+ }
101
+ }
102
+ //# sourceMappingURL=promptApi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promptApi.js","sourceRoot":"","sources":["../src/promptApi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export { formatToolError } from "./toolErrors.js";
3
+ export interface GarminMcpServer {
4
+ start: () => Promise<void>;
5
+ close: () => Promise<void>;
6
+ }
7
+ export declare function registerGarminTools(mcpServer: McpServer): void;
8
+ export declare function createMcpServerInstance(): McpServer;
9
+ export declare function createMcpServer(): GarminMcpServer;
10
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AASpE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAuC9D;AAED,wBAAgB,uBAAuB,IAAI,SAAS,CAQnD;AAED,wBAAgB,eAAe,IAAI,eAAe,CAgBjD"}
package/dist/server.js ADDED
@@ -0,0 +1,67 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { executeTool, toolRegistry, toolSchemas } from "./tools/index.js";
4
+ import { logger } from "./utils/logger.js";
5
+ import { packageVersion } from "./version.js";
6
+ import { formatToolError } from "./toolErrors.js";
7
+ // SECTION: MCP Server
8
+ export { formatToolError } from "./toolErrors.js";
9
+ export function registerGarminTools(mcpServer) {
10
+ for (const tool of toolRegistry) {
11
+ const schema = toolSchemas[tool.name];
12
+ mcpServer.registerTool(tool.name, {
13
+ description: tool.description,
14
+ inputSchema: schema,
15
+ }, async (args) => {
16
+ try {
17
+ const result = await executeTool(tool.name, args);
18
+ return {
19
+ content: [
20
+ {
21
+ type: "text",
22
+ text: result.text,
23
+ },
24
+ ],
25
+ };
26
+ }
27
+ catch (error) {
28
+ logger.error({ error, tool: tool.name }, "Tool execution failed");
29
+ const message = formatToolError(error);
30
+ return {
31
+ isError: true,
32
+ content: [
33
+ {
34
+ type: "text",
35
+ text: message,
36
+ },
37
+ ],
38
+ };
39
+ }
40
+ });
41
+ }
42
+ }
43
+ export function createMcpServerInstance() {
44
+ const mcpServer = new McpServer({
45
+ name: "garmin-bud",
46
+ version: packageVersion,
47
+ });
48
+ registerGarminTools(mcpServer);
49
+ return mcpServer;
50
+ }
51
+ export function createMcpServer() {
52
+ let mcpServer = createMcpServerInstance();
53
+ let transport = null;
54
+ return {
55
+ async start() {
56
+ mcpServer = createMcpServerInstance();
57
+ transport = new StdioServerTransport();
58
+ await mcpServer.connect(transport);
59
+ logger.info("GarminBud server started on stdio transport");
60
+ },
61
+ async close() {
62
+ await mcpServer.close();
63
+ transport = null;
64
+ },
65
+ };
66
+ }
67
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,sBAAsB;AAEtB,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAOlD,MAAM,UAAU,mBAAmB,CAAC,SAAoB;IACtD,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,IAAgC,CAAC,CAAC;QAElE,SAAS,CAAC,YAAY,CACpB,IAAI,CAAC,IAAI,EACT;YACE,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,MAAM;SACpB,EACD,KAAK,EAAE,IAA6B,EAAE,EAAE;YACtC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBAElD,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,MAAM,CAAC,IAAI;yBAClB;qBACF;iBACF,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,uBAAuB,CAAC,CAAC;gBAElE,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;gBACvC,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,OAAO;yBACd;qBACF;iBACF,CAAC;YACJ,CAAC;QACH,CAAC,CACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC;QAC9B,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,cAAc;KACxB,CAAC,CAAC;IAEH,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAC/B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,IAAI,SAAS,GAAG,uBAAuB,EAAE,CAAC;IAC1C,IAAI,SAAS,GAAgC,IAAI,CAAC;IAElD,OAAO;QACL,KAAK,CAAC,KAAK;YACT,SAAS,GAAG,uBAAuB,EAAE,CAAC;YACtC,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;YACvC,MAAM,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;QAC7D,CAAC;QACD,KAAK,CAAC,KAAK;YACT,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;YACxB,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function runSetup(): Promise<void>;
2
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AA4KA,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAsE9C"}
package/dist/setup.js ADDED
@@ -0,0 +1,194 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { stdin as input, stdout as output } from "node:process";
3
+ import fs from "node:fs";
4
+ import { authenticateGarmin, clearStoredSession } from "./garmin/auth.js";
5
+ import { resetGarminClient } from "./garmin/client.js";
6
+ import { assertGarminCredentials, getDistIndexPath, writeEnvFile, appConfig, } from "./config.js";
7
+ import { configureLogger } from "./utils/logger.js";
8
+ import { configureGarminBudForClient, detectMcpClients, } from "./mcpConfig.js";
9
+ import { printLiveCheckResults, runLiveCheck } from "./check.js";
10
+ // SECTION: Interactive Setup
11
+ async function promptLine(rl, question) {
12
+ const answer = (await rl.question(question)).trim();
13
+ return answer;
14
+ }
15
+ async function promptSecret(question) {
16
+ return new Promise((resolve) => {
17
+ output.write(question);
18
+ if (!input.isTTY || typeof input.setRawMode !== "function") {
19
+ void (async () => {
20
+ const rl = createInterface({ input, output });
21
+ const answer = (await rl.question("")).trim();
22
+ rl.close();
23
+ resolve(answer);
24
+ })();
25
+ return;
26
+ }
27
+ input.resume();
28
+ input.setRawMode(true);
29
+ input.setEncoding("utf8");
30
+ let password = "";
31
+ const onData = (chunk) => {
32
+ const char = chunk;
33
+ if (char === "\n" || char === "\r" || char === "\u0004") {
34
+ input.setRawMode(false);
35
+ input.removeListener("data", onData);
36
+ output.write("\n");
37
+ resolve(password);
38
+ return;
39
+ }
40
+ if (char === "\u0003") {
41
+ process.exit(130);
42
+ }
43
+ if (char === "\u007F" || char === "\b") {
44
+ password = password.slice(0, -1);
45
+ return;
46
+ }
47
+ password += char;
48
+ output.write("*");
49
+ };
50
+ input.on("data", onData);
51
+ });
52
+ }
53
+ function isValidEmail(value) {
54
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
55
+ }
56
+ async function promptCredentials() {
57
+ const rl = createInterface({ input, output });
58
+ try {
59
+ output.write("\nGarminBud setup\n");
60
+ output.write("This wizard will save credentials locally, authenticate with Garmin Connect,\n");
61
+ output.write("and optionally connect GarminBud to Cursor or Claude Desktop.\n\n");
62
+ output.write("Note: MFA must be disabled on your Garmin Connect account.\n\n");
63
+ let email = "";
64
+ while (!email) {
65
+ const value = await promptLine(rl, "Garmin Connect email: ");
66
+ if (!value) {
67
+ output.write("Email is required.\n");
68
+ continue;
69
+ }
70
+ if (!isValidEmail(value)) {
71
+ output.write("Enter a valid email address.\n");
72
+ continue;
73
+ }
74
+ email = value;
75
+ }
76
+ rl.close();
77
+ let password = "";
78
+ while (!password) {
79
+ password = await promptSecret("Garmin Connect password: ");
80
+ if (!password) {
81
+ output.write("Password is required.\n");
82
+ }
83
+ }
84
+ return { email, password };
85
+ }
86
+ finally {
87
+ rl.close();
88
+ }
89
+ }
90
+ async function promptMcpClients(clients) {
91
+ if (clients.length === 0) {
92
+ output.write("\nNo supported MCP clients were detected on this machine.\n");
93
+ output.write("You can add GarminBud manually later using QUICKSTART.md.\n");
94
+ return [];
95
+ }
96
+ const rl = createInterface({ input, output });
97
+ try {
98
+ output.write("\nDetected MCP clients:\n");
99
+ clients.forEach((client, index) => {
100
+ output.write(` ${index + 1}. ${client.label} (${client.configPath})\n`);
101
+ });
102
+ output.write(" A. All detected clients\n");
103
+ output.write(" S. Skip MCP client setup\n\n");
104
+ while (true) {
105
+ const answer = (await rl.question("Configure which client? [1/A/S]: ")).trim().toLowerCase();
106
+ if (answer === "s" || answer === "skip" || answer === "") {
107
+ return [];
108
+ }
109
+ if (answer === "a" || answer === "all") {
110
+ return clients;
111
+ }
112
+ const index = Number.parseInt(answer, 10);
113
+ if (Number.isFinite(index) && index >= 1 && index <= clients.length) {
114
+ return [clients[index - 1]];
115
+ }
116
+ output.write("Enter a number from the list, A for all, or S to skip.\n");
117
+ }
118
+ }
119
+ finally {
120
+ rl.close();
121
+ }
122
+ }
123
+ async function promptRunLiveCheck() {
124
+ const rl = createInterface({ input, output });
125
+ try {
126
+ const answer = (await rl.question("\nRun a live Garmin API check now? [Y/n]: ")).trim().toLowerCase();
127
+ return answer === "" || answer === "y" || answer === "yes";
128
+ }
129
+ finally {
130
+ rl.close();
131
+ }
132
+ }
133
+ export async function runSetup() {
134
+ const credentials = await promptCredentials();
135
+ const envPath = writeEnvFile(credentials);
136
+ output.write(`\nSaved credentials to ${envPath}\n`);
137
+ output.write("Authenticating with Garmin Connect...\n");
138
+ configureLogger(appConfig.logPath);
139
+ clearStoredSession();
140
+ resetGarminClient();
141
+ try {
142
+ await authenticateGarmin(true);
143
+ }
144
+ catch (error) {
145
+ const message = error instanceof Error ? error.message : "Authentication failed";
146
+ output.write(`\nAuthentication failed.\n${message}\n`);
147
+ output.write("\nCommon fixes:\n");
148
+ output.write("- Verify your email and password\n");
149
+ output.write("- Disable MFA at connect.garmin.com → Account Settings → Security\n");
150
+ output.write("- Run `garmin-bud setup` again after fixing credentials\n");
151
+ process.exitCode = 1;
152
+ return;
153
+ }
154
+ output.write("Garmin authentication successful. Session saved.\n");
155
+ const distIndexPath = getDistIndexPath();
156
+ if (!fs.existsSync(distIndexPath)) {
157
+ output.write(`\nWarning: ${distIndexPath} was not found. Run \`npm run build\` before starting the MCP server.\n`);
158
+ }
159
+ const detectedClients = detectMcpClients();
160
+ const selectedClients = await promptMcpClients(detectedClients);
161
+ for (const client of selectedClients) {
162
+ configureGarminBudForClient(client, {
163
+ email: credentials.email,
164
+ password: credentials.password,
165
+ distIndexPath,
166
+ });
167
+ output.write(`Configured GarminBud in ${client.label} (${client.configPath})\n`);
168
+ }
169
+ output.write("\nSetup complete.\n");
170
+ output.write("\nWeb AI connector (claude.ai, ChatGPT):\n");
171
+ output.write(`- API key saved to .env as GARMIN_MCP_API_KEY\n`);
172
+ output.write(`- Run: garmin-bud serve\n`);
173
+ output.write(`- See docs/WEB-MCP.md for Cloudflare Tunnel + claude.ai setup\n`);
174
+ output.write(`- Your API key: ${appConfig.mcpApiKey}\n`);
175
+ if (selectedClients.length > 0) {
176
+ output.write("\nNext steps:\n");
177
+ output.write("- Restart your MCP client completely so it picks up the new server\n");
178
+ output.write("- Ask things like \"What was my last workout?\" or \"How did I sleep this week?\"\n");
179
+ }
180
+ else {
181
+ output.write("\nNext steps:\n");
182
+ output.write("- Run `garmin-bud start` for manual MCP usage, or follow QUICKSTART.md to connect a client\n");
183
+ }
184
+ if (await promptRunLiveCheck()) {
185
+ assertGarminCredentials();
186
+ const results = await runLiveCheck();
187
+ output.write("\n");
188
+ printLiveCheckResults(results);
189
+ if (results.some((result) => !result.ok)) {
190
+ process.exitCode = 1;
191
+ }
192
+ }
193
+ }
194
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function formatToolError(error: unknown): string;
2
+ //# sourceMappingURL=toolErrors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolErrors.d.ts","sourceRoot":"","sources":["../src/toolErrors.ts"],"names":[],"mappings":"AAGA,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAiBtD"}
@@ -0,0 +1,18 @@
1
+ import { GarminApiError } from "./garmin/types.js";
2
+ import { sanitizeErrorMessage } from "./utils/helpers.js";
3
+ export function formatToolError(error) {
4
+ if (error instanceof GarminApiError) {
5
+ if (error.statusCode === 429) {
6
+ return `${error.message} Retry in ${error.retryAfterSeconds ?? 60} seconds.`;
7
+ }
8
+ return sanitizeErrorMessage(error.message);
9
+ }
10
+ if (error instanceof Error) {
11
+ if (error.message.toLowerCase().includes("authentication")) {
12
+ return `${sanitizeErrorMessage(error.message)} Run "garmin-bud auth" to re-authenticate.`;
13
+ }
14
+ return sanitizeErrorMessage(error.message);
15
+ }
16
+ return "An unexpected error occurred while executing the Garmin tool.";
17
+ }
18
+ //# sourceMappingURL=toolErrors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolErrors.js","sourceRoot":"","sources":["../src/toolErrors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,KAAK,YAAY,cAAc,EAAE,CAAC;QACpC,IAAI,KAAK,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;YAC7B,OAAO,GAAG,KAAK,CAAC,OAAO,aAAa,KAAK,CAAC,iBAAiB,IAAI,EAAE,WAAW,CAAC;QAC/E,CAAC;QACD,OAAO,oBAAoB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC3D,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,OAAO,CAAC,4CAA4C,CAAC;QAC5F,CAAC;QAED,OAAO,oBAAoB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,+DAA+D,CAAC;AACzE,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { ActivitySummary, ToolTextResult } from "../garmin/types.js";
2
+ import type { ToolDefinition } from "./types.js";
3
+ export declare function formatActivitySummary(activity: ActivitySummary): string;
4
+ export declare function getActivitiesPool(): Promise<{
5
+ activities: ActivitySummary[];
6
+ truncated: boolean;
7
+ }>;
8
+ export declare function getLatestActivity(): Promise<ToolTextResult>;
9
+ export declare function getActivitiesRange(input: Record<string, unknown>): Promise<ToolTextResult>;
10
+ export declare const activityToolDefinitions: ToolDefinition[];
11
+ //# sourceMappingURL=activities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activities.d.ts","sourceRoot":"","sources":["../../src/tools/activities.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA+BjD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CAavE;AAkCD,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IAAE,UAAU,EAAE,eAAe,EAAE,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAIxG;AAID,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,cAAc,CAAC,CAmBjE;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAgChG;AAED,eAAO,MAAM,uBAAuB,EAAE,cAAc,EAsBnD,CAAC"}
@@ -0,0 +1,134 @@
1
+ import { appConfig } from "../config.js";
2
+ import { buildToolCacheKey, withCache } from "../garmin/cache.js";
3
+ import { withGarminClient } from "../garmin/client.js";
4
+ import { filterActivitiesByRange, formatDistanceMeters, formatDuration, formatIsoDate, formatPaceMetersPerSecond, parseActivityLocalDateTime, } from "../utils/helpers.js";
5
+ const ACTIVITIES_PAGE_SIZE = 100;
6
+ const MAX_ACTIVITIES_FETCH = 500;
7
+ // SECTION: Activity Mapping
8
+ function mapActivity(activity) {
9
+ return {
10
+ activityId: activity.activityId,
11
+ name: activity.activityName,
12
+ type: activity.activityType.typeKey,
13
+ startTimeLocal: activity.startTimeLocal,
14
+ distanceMeters: activity.distance,
15
+ durationSeconds: activity.duration,
16
+ averageHeartRate: activity.averageHR ?? null,
17
+ maxHeartRate: activity.maxHR ?? null,
18
+ elevationGainMeters: activity.elevationGain,
19
+ calories: activity.calories,
20
+ averageSpeedMps: activity.averageSpeed,
21
+ };
22
+ }
23
+ export function formatActivitySummary(activity) {
24
+ return [
25
+ `Activity: ${activity.name}`,
26
+ `Type: ${activity.type}`,
27
+ `Date: ${activity.startTimeLocal}`,
28
+ `Distance: ${formatDistanceMeters(activity.distanceMeters)}`,
29
+ `Duration: ${formatDuration(activity.durationSeconds)}`,
30
+ `Pace: ${formatPaceMetersPerSecond(activity.averageSpeedMps)}`,
31
+ `Avg HR: ${activity.averageHeartRate ?? "n/a"} bpm`,
32
+ `Max HR: ${activity.maxHeartRate ?? "n/a"} bpm`,
33
+ `Elevation gain: ${activity.elevationGainMeters.toFixed(0)} m`,
34
+ `Calories: ${activity.calories}`,
35
+ ].join("\n");
36
+ }
37
+ async function fetchActivitiesPage(start, limit) {
38
+ return withGarminClient(async (client) => {
39
+ const activities = await client.getActivities(start, limit);
40
+ return activities.map(mapActivity);
41
+ });
42
+ }
43
+ async function fetchActivitiesPool() {
44
+ const all = [];
45
+ let start = 0;
46
+ while (all.length < MAX_ACTIVITIES_FETCH) {
47
+ const page = await fetchActivitiesPage(start, ACTIVITIES_PAGE_SIZE);
48
+ if (page.length === 0) {
49
+ break;
50
+ }
51
+ all.push(...page);
52
+ if (page.length < ACTIVITIES_PAGE_SIZE) {
53
+ break;
54
+ }
55
+ start += ACTIVITIES_PAGE_SIZE;
56
+ }
57
+ return {
58
+ activities: all,
59
+ truncated: all.length >= MAX_ACTIVITIES_FETCH,
60
+ };
61
+ }
62
+ export async function getActivitiesPool() {
63
+ const cacheKey = buildToolCacheKey("activities_pool", {});
64
+ return withCache(cacheKey, appConfig.cacheTtlActivities, fetchActivitiesPool);
65
+ }
66
+ // SECTION: Tool Handlers
67
+ export async function getLatestActivity() {
68
+ const cacheKey = buildToolCacheKey("get_latest_activity", {});
69
+ const activity = await withCache(cacheKey, appConfig.cacheTtlActivities, async () => {
70
+ const { activities } = await getActivitiesPool();
71
+ return activities[0] ?? null;
72
+ });
73
+ if (!activity) {
74
+ return {
75
+ type: "text",
76
+ text: "No activities found in your Garmin Connect account.",
77
+ };
78
+ }
79
+ return {
80
+ type: "text",
81
+ text: formatActivitySummary(activity),
82
+ };
83
+ }
84
+ export async function getActivitiesRange(input) {
85
+ const start_date = input.start_date;
86
+ const end_date = input.end_date;
87
+ const { activities: pool, truncated } = await getActivitiesPool();
88
+ const activities = filterActivitiesByRange(pool, start_date, end_date);
89
+ if (activities.length === 0) {
90
+ return {
91
+ type: "text",
92
+ text: `No activities found between ${start_date} and ${end_date}.`,
93
+ };
94
+ }
95
+ const lines = activities.map((activity, index) => {
96
+ const activityDate = parseActivityLocalDateTime(activity.startTimeLocal).toISODate() ??
97
+ formatIsoDate(new Date(activity.startTimeLocal));
98
+ return [
99
+ `${index + 1}. ${activity.name} (${activity.type})`,
100
+ ` ${activityDate} | ${formatDistanceMeters(activity.distanceMeters)} | ${formatDuration(activity.durationSeconds)}`,
101
+ ].join("\n");
102
+ });
103
+ const warning = truncated
104
+ ? "\n\nNote: Results may be incomplete — only the most recent 500 activities were scanned."
105
+ : "";
106
+ return {
107
+ type: "text",
108
+ text: [`Found ${activities.length} activities:`, "", ...lines].join("\n") + warning,
109
+ };
110
+ }
111
+ export const activityToolDefinitions = [
112
+ {
113
+ name: "get_latest_activity",
114
+ description: "Returns the most recent Garmin activity with distance, duration, pace, and heart rate stats.",
115
+ inputSchema: {},
116
+ handler: getLatestActivity,
117
+ },
118
+ {
119
+ name: "get_activities_range",
120
+ description: "Returns Garmin activities within an ISO 8601 date range.",
121
+ inputSchema: {
122
+ start_date: {
123
+ type: "string",
124
+ description: "Start date in ISO 8601 format, e.g. 2026-06-01",
125
+ },
126
+ end_date: {
127
+ type: "string",
128
+ description: "End date in ISO 8601 format, e.g. 2026-06-07",
129
+ },
130
+ },
131
+ handler: getActivitiesRange,
132
+ },
133
+ ];
134
+ //# sourceMappingURL=activities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activities.js","sourceRoot":"","sources":["../../src/tools/activities.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ToolTextResult } from "../garmin/types.js";
2
+ import type { ToolDefinition } from "./types.js";
3
+ export declare function getBodyComposition(input: {
4
+ days?: number;
5
+ }): Promise<ToolTextResult>;
6
+ export declare const bodyCompositionToolDefinitions: ToolDefinition[];
7
+ //# sourceMappingURL=bodyComposition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bodyComposition.d.ts","sourceRoot":"","sources":["../../src/tools/bodyComposition.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAwB,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAwCjD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CA0E1F;AAED,eAAO,MAAM,8BAA8B,EAAE,cAAc,EAY1D,CAAC"}