trainbud 0.5.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 (274) hide show
  1. package/.env.example +11 -0
  2. package/CHANGELOG.md +675 -0
  3. package/LICENSE +21 -0
  4. package/QUICKSTART.md +182 -0
  5. package/README.md +322 -0
  6. package/dist/appDb.d.ts +83 -0
  7. package/dist/appDb.d.ts.map +1 -0
  8. package/dist/appDb.js +239 -0
  9. package/dist/appDb.js.map +1 -0
  10. package/dist/check.d.ts +12 -0
  11. package/dist/check.d.ts.map +1 -0
  12. package/dist/check.js +230 -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 +457 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/config.d.ts +61 -0
  19. package/dist/config.d.ts.map +1 -0
  20. package/dist/config.js +241 -0
  21. package/dist/config.js.map +1 -0
  22. package/dist/dashboard.d.ts +8 -0
  23. package/dist/dashboard.d.ts.map +1 -0
  24. package/dist/dashboard.js +1004 -0
  25. package/dist/dashboard.js.map +1 -0
  26. package/dist/dashboardCharts.d.ts +99 -0
  27. package/dist/dashboardCharts.d.ts.map +1 -0
  28. package/dist/dashboardCharts.js +317 -0
  29. package/dist/dashboardCharts.js.map +1 -0
  30. package/dist/dashboardData.d.ts +89 -0
  31. package/dist/dashboardData.d.ts.map +1 -0
  32. package/dist/dashboardData.js +228 -0
  33. package/dist/dashboardData.js.map +1 -0
  34. package/dist/detect/baseline.d.ts +17 -0
  35. package/dist/detect/baseline.d.ts.map +1 -0
  36. package/dist/detect/baseline.js +62 -0
  37. package/dist/detect/baseline.js.map +1 -0
  38. package/dist/detect/countdown.d.ts +30 -0
  39. package/dist/detect/countdown.d.ts.map +1 -0
  40. package/dist/detect/countdown.js +79 -0
  41. package/dist/detect/countdown.js.map +1 -0
  42. package/dist/detect/detectors.d.ts +20 -0
  43. package/dist/detect/detectors.d.ts.map +1 -0
  44. package/dist/detect/detectors.js +291 -0
  45. package/dist/detect/detectors.js.map +1 -0
  46. package/dist/detect/findings.d.ts +29 -0
  47. package/dist/detect/findings.d.ts.map +1 -0
  48. package/dist/detect/findings.js +2 -0
  49. package/dist/detect/findings.js.map +1 -0
  50. package/dist/detect/forecast.d.ts +33 -0
  51. package/dist/detect/forecast.d.ts.map +1 -0
  52. package/dist/detect/forecast.js +122 -0
  53. package/dist/detect/forecast.js.map +1 -0
  54. package/dist/detect/index.d.ts +50 -0
  55. package/dist/detect/index.d.ts.map +1 -0
  56. package/dist/detect/index.js +114 -0
  57. package/dist/detect/index.js.map +1 -0
  58. package/dist/detect/sleepQuality.d.ts +21 -0
  59. package/dist/detect/sleepQuality.d.ts.map +1 -0
  60. package/dist/detect/sleepQuality.js +164 -0
  61. package/dist/detect/sleepQuality.js.map +1 -0
  62. package/dist/detect/trimp.d.ts +22 -0
  63. package/dist/detect/trimp.d.ts.map +1 -0
  64. package/dist/detect/trimp.js +77 -0
  65. package/dist/detect/trimp.js.map +1 -0
  66. package/dist/detect/week.d.ts +31 -0
  67. package/dist/detect/week.d.ts.map +1 -0
  68. package/dist/detect/week.js +184 -0
  69. package/dist/detect/week.js.map +1 -0
  70. package/dist/garmin/auth.d.ts +10 -0
  71. package/dist/garmin/auth.d.ts.map +1 -0
  72. package/dist/garmin/auth.js +81 -0
  73. package/dist/garmin/auth.js.map +1 -0
  74. package/dist/garmin/cache.d.ts +42 -0
  75. package/dist/garmin/cache.d.ts.map +1 -0
  76. package/dist/garmin/cache.js +141 -0
  77. package/dist/garmin/cache.js.map +1 -0
  78. package/dist/garmin/client.d.ts +22 -0
  79. package/dist/garmin/client.d.ts.map +1 -0
  80. package/dist/garmin/client.js +237 -0
  81. package/dist/garmin/client.js.map +1 -0
  82. package/dist/garmin/daily.d.ts +24 -0
  83. package/dist/garmin/daily.d.ts.map +1 -0
  84. package/dist/garmin/daily.js +96 -0
  85. package/dist/garmin/daily.js.map +1 -0
  86. package/dist/garmin/garminApiTypes.d.ts +70 -0
  87. package/dist/garmin/garminApiTypes.d.ts.map +1 -0
  88. package/dist/garmin/garminApiTypes.js +3 -0
  89. package/dist/garmin/garminApiTypes.js.map +1 -0
  90. package/dist/garmin/garminConnect.d.ts +17 -0
  91. package/dist/garmin/garminConnect.d.ts.map +1 -0
  92. package/dist/garmin/garminConnect.js +5 -0
  93. package/dist/garmin/garminConnect.js.map +1 -0
  94. package/dist/garmin/partial.d.ts +32 -0
  95. package/dist/garmin/partial.d.ts.map +1 -0
  96. package/dist/garmin/partial.js +38 -0
  97. package/dist/garmin/partial.js.map +1 -0
  98. package/dist/garmin/rawApi.d.ts +59 -0
  99. package/dist/garmin/rawApi.d.ts.map +1 -0
  100. package/dist/garmin/rawApi.js +113 -0
  101. package/dist/garmin/rawApi.js.map +1 -0
  102. package/dist/garmin/types.d.ts +85 -0
  103. package/dist/garmin/types.d.ts.map +1 -0
  104. package/dist/garmin/types.js +11 -0
  105. package/dist/garmin/types.js.map +1 -0
  106. package/dist/history/capture.d.ts +12 -0
  107. package/dist/history/capture.d.ts.map +1 -0
  108. package/dist/history/capture.js +63 -0
  109. package/dist/history/capture.js.map +1 -0
  110. package/dist/history/context.d.ts +51 -0
  111. package/dist/history/context.d.ts.map +1 -0
  112. package/dist/history/context.js +122 -0
  113. package/dist/history/context.js.map +1 -0
  114. package/dist/history/fallback.d.ts +72 -0
  115. package/dist/history/fallback.d.ts.map +1 -0
  116. package/dist/history/fallback.js +185 -0
  117. package/dist/history/fallback.js.map +1 -0
  118. package/dist/history/ingest.d.ts +44 -0
  119. package/dist/history/ingest.d.ts.map +1 -0
  120. package/dist/history/ingest.js +347 -0
  121. package/dist/history/ingest.js.map +1 -0
  122. package/dist/history/scheduler.d.ts +20 -0
  123. package/dist/history/scheduler.d.ts.map +1 -0
  124. package/dist/history/scheduler.js +54 -0
  125. package/dist/history/scheduler.js.map +1 -0
  126. package/dist/history/schema.d.ts +41 -0
  127. package/dist/history/schema.d.ts.map +1 -0
  128. package/dist/history/schema.js +91 -0
  129. package/dist/history/schema.js.map +1 -0
  130. package/dist/history/store.d.ts +112 -0
  131. package/dist/history/store.d.ts.map +1 -0
  132. package/dist/history/store.js +282 -0
  133. package/dist/history/store.js.map +1 -0
  134. package/dist/httpServer.d.ts +55 -0
  135. package/dist/httpServer.d.ts.map +1 -0
  136. package/dist/httpServer.js +1027 -0
  137. package/dist/httpServer.js.map +1 -0
  138. package/dist/index.d.ts +3 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +11 -0
  141. package/dist/index.js.map +1 -0
  142. package/dist/mcpConfig.d.ts +56 -0
  143. package/dist/mcpConfig.d.ts.map +1 -0
  144. package/dist/mcpConfig.js +106 -0
  145. package/dist/mcpConfig.js.map +1 -0
  146. package/dist/pairApi.d.ts +14 -0
  147. package/dist/pairApi.d.ts.map +1 -0
  148. package/dist/pairApi.js +38 -0
  149. package/dist/pairApi.js.map +1 -0
  150. package/dist/paths.d.ts +45 -0
  151. package/dist/paths.d.ts.map +1 -0
  152. package/dist/paths.js +105 -0
  153. package/dist/paths.js.map +1 -0
  154. package/dist/profile.d.ts +123 -0
  155. package/dist/profile.d.ts.map +1 -0
  156. package/dist/profile.js +327 -0
  157. package/dist/profile.js.map +1 -0
  158. package/dist/promptApi.d.ts +43 -0
  159. package/dist/promptApi.d.ts.map +1 -0
  160. package/dist/promptApi.js +328 -0
  161. package/dist/promptApi.js.map +1 -0
  162. package/dist/promptSuggestions.d.ts +16 -0
  163. package/dist/promptSuggestions.d.ts.map +1 -0
  164. package/dist/promptSuggestions.js +141 -0
  165. package/dist/promptSuggestions.js.map +1 -0
  166. package/dist/selfTest.d.ts +53 -0
  167. package/dist/selfTest.d.ts.map +1 -0
  168. package/dist/selfTest.js +225 -0
  169. package/dist/selfTest.js.map +1 -0
  170. package/dist/server.d.ts +10 -0
  171. package/dist/server.d.ts.map +1 -0
  172. package/dist/server.js +67 -0
  173. package/dist/server.js.map +1 -0
  174. package/dist/setup.d.ts +2 -0
  175. package/dist/setup.d.ts.map +1 -0
  176. package/dist/setup.js +210 -0
  177. package/dist/setup.js.map +1 -0
  178. package/dist/toolErrors.d.ts +2 -0
  179. package/dist/toolErrors.d.ts.map +1 -0
  180. package/dist/toolErrors.js +18 -0
  181. package/dist/toolErrors.js.map +1 -0
  182. package/dist/tools/activities.d.ts +18 -0
  183. package/dist/tools/activities.d.ts.map +1 -0
  184. package/dist/tools/activities.js +182 -0
  185. package/dist/tools/activities.js.map +1 -0
  186. package/dist/tools/bodyComposition.d.ts +10 -0
  187. package/dist/tools/bodyComposition.d.ts.map +1 -0
  188. package/dist/tools/bodyComposition.js +139 -0
  189. package/dist/tools/bodyComposition.js.map +1 -0
  190. package/dist/tools/context.d.ts +8 -0
  191. package/dist/tools/context.d.ts.map +1 -0
  192. package/dist/tools/context.js +148 -0
  193. package/dist/tools/context.js.map +1 -0
  194. package/dist/tools/findings.d.ts +7 -0
  195. package/dist/tools/findings.d.ts.map +1 -0
  196. package/dist/tools/findings.js +51 -0
  197. package/dist/tools/findings.js.map +1 -0
  198. package/dist/tools/heartRate.d.ts +10 -0
  199. package/dist/tools/heartRate.d.ts.map +1 -0
  200. package/dist/tools/heartRate.js +105 -0
  201. package/dist/tools/heartRate.js.map +1 -0
  202. package/dist/tools/index.d.ts +66 -0
  203. package/dist/tools/index.d.ts.map +1 -0
  204. package/dist/tools/index.js +101 -0
  205. package/dist/tools/index.js.map +1 -0
  206. package/dist/tools/payloads.d.ts +148 -0
  207. package/dist/tools/payloads.d.ts.map +1 -0
  208. package/dist/tools/payloads.js +2 -0
  209. package/dist/tools/payloads.js.map +1 -0
  210. package/dist/tools/recovery.d.ts +31 -0
  211. package/dist/tools/recovery.d.ts.map +1 -0
  212. package/dist/tools/recovery.js +350 -0
  213. package/dist/tools/recovery.js.map +1 -0
  214. package/dist/tools/sleep.d.ts +10 -0
  215. package/dist/tools/sleep.d.ts.map +1 -0
  216. package/dist/tools/sleep.js +124 -0
  217. package/dist/tools/sleep.js.map +1 -0
  218. package/dist/tools/stress.d.ts +11 -0
  219. package/dist/tools/stress.d.ts.map +1 -0
  220. package/dist/tools/stress.js +108 -0
  221. package/dist/tools/stress.js.map +1 -0
  222. package/dist/tools/trainingInsights.d.ts +15 -0
  223. package/dist/tools/trainingInsights.d.ts.map +1 -0
  224. package/dist/tools/trainingInsights.js +79 -0
  225. package/dist/tools/trainingInsights.js.map +1 -0
  226. package/dist/tools/types.d.ts +12 -0
  227. package/dist/tools/types.d.ts.map +1 -0
  228. package/dist/tools/types.js +2 -0
  229. package/dist/tools/types.js.map +1 -0
  230. package/dist/tools/vo2Max.d.ts +11 -0
  231. package/dist/tools/vo2Max.d.ts.map +1 -0
  232. package/dist/tools/vo2Max.js +110 -0
  233. package/dist/tools/vo2Max.js.map +1 -0
  234. package/dist/tools/week.d.ts +12 -0
  235. package/dist/tools/week.d.ts.map +1 -0
  236. package/dist/tools/week.js +74 -0
  237. package/dist/tools/week.js.map +1 -0
  238. package/dist/usage.d.ts +104 -0
  239. package/dist/usage.d.ts.map +1 -0
  240. package/dist/usage.js +247 -0
  241. package/dist/usage.js.map +1 -0
  242. package/dist/utils/batch.d.ts +2 -0
  243. package/dist/utils/batch.d.ts.map +1 -0
  244. package/dist/utils/batch.js +18 -0
  245. package/dist/utils/batch.js.map +1 -0
  246. package/dist/utils/helpers.d.ts +32 -0
  247. package/dist/utils/helpers.d.ts.map +1 -0
  248. package/dist/utils/helpers.js +188 -0
  249. package/dist/utils/helpers.js.map +1 -0
  250. package/dist/utils/jsonFile.d.ts +19 -0
  251. package/dist/utils/jsonFile.d.ts.map +1 -0
  252. package/dist/utils/jsonFile.js +24 -0
  253. package/dist/utils/jsonFile.js.map +1 -0
  254. package/dist/utils/logger.d.ts +4 -0
  255. package/dist/utils/logger.d.ts.map +1 -0
  256. package/dist/utils/logger.js +97 -0
  257. package/dist/utils/logger.js.map +1 -0
  258. package/dist/utils/secretFile.d.ts +15 -0
  259. package/dist/utils/secretFile.d.ts.map +1 -0
  260. package/dist/utils/secretFile.js +47 -0
  261. package/dist/utils/secretFile.js.map +1 -0
  262. package/dist/utils/stdio.d.ts +10 -0
  263. package/dist/utils/stdio.d.ts.map +1 -0
  264. package/dist/utils/stdio.js +70 -0
  265. package/dist/utils/stdio.js.map +1 -0
  266. package/dist/version.d.ts +2 -0
  267. package/dist/version.d.ts.map +1 -0
  268. package/dist/version.js +7 -0
  269. package/dist/version.js.map +1 -0
  270. package/dist/watchApi.d.ts +264 -0
  271. package/dist/watchApi.d.ts.map +1 -0
  272. package/dist/watchApi.js +333 -0
  273. package/dist/watchApi.js.map +1 -0
  274. package/package.json +77 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trainingInsights.d.ts","sourceRoot":"","sources":["../../src/tools/trainingInsights.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,uBAAuB,EAChC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,MAAM,CA2BR;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACvB,OAAO,CAAC,UAAU,CAAC,uBAAuB,CAAC,CAAC,CA6B9C;AAED,eAAO,MAAM,+BAA+B,EAAE,cAAc,EAa3D,CAAC"}
@@ -0,0 +1,79 @@
1
+ import { filterActivitiesByRange } from "../utils/helpers.js";
2
+ import { DateTime } from "luxon";
3
+ import { getActivitiesPool, formatActivitySummary } from "./activities.js";
4
+ import { getProfile } from "../profile.js";
5
+ import { getSleepDataTool } from "./sleep.js";
6
+ import { getRecoveryStatus } from "./recovery.js";
7
+ import { getStressLevels } from "./stress.js";
8
+ /**
9
+ * The sub-tools' rendered text is passed in rather than re-derived here. This
10
+ * tool embeds their output verbatim, so calling three renderers from a fourth
11
+ * would duplicate the composition the handler already does -- and would make
12
+ * this function impure for no gain.
13
+ */
14
+ export function renderTrainingInsightsText(payload, sleepText, recoveryText, stressText) {
15
+ const activityLines = payload.activities.length > 0
16
+ ? payload.activities.map((activity, index) => {
17
+ return `${index + 1}. ${activity.name} (${activity.type}) — ${activity.startTimeLocal}`;
18
+ })
19
+ : [`No activities found between ${payload.startDate} and ${payload.endDate}.`];
20
+ return [
21
+ "Training insights summary",
22
+ `Period: ${payload.startDate} to ${payload.endDate}`,
23
+ "",
24
+ "## Latest activity",
25
+ payload.latest ? formatActivitySummary(payload.latest, getProfile().units) : "No activities found.",
26
+ "",
27
+ "## Activities in period",
28
+ ...activityLines,
29
+ "",
30
+ "## Sleep",
31
+ sleepText,
32
+ "",
33
+ "## Recovery",
34
+ recoveryText,
35
+ "",
36
+ "## Stress",
37
+ stressText,
38
+ ].join("\n");
39
+ }
40
+ export async function getTrainingInsights(input) {
41
+ const days = input.days ?? 7;
42
+ const endDate = DateTime.now().toISODate() ?? "";
43
+ const startDate = DateTime.now().minus({ days }).toISODate() ?? "";
44
+ const { activities: pool } = await getActivitiesPool();
45
+ const ranged = filterActivitiesByRange(pool, startDate, endDate);
46
+ const [sleep, recovery, stress] = await Promise.all([
47
+ getSleepDataTool({ nights: days }),
48
+ getRecoveryStatus({}),
49
+ getStressLevels({ days }),
50
+ ]);
51
+ const payload = {
52
+ startDate,
53
+ endDate,
54
+ latest: pool[0] ?? null,
55
+ activities: ranged,
56
+ sleep: sleep.data ?? null,
57
+ recovery: recovery.data ?? null,
58
+ stress: stress.data ?? null,
59
+ };
60
+ return {
61
+ type: "text",
62
+ text: renderTrainingInsightsText(payload, sleep.text, recovery.text, stress.text),
63
+ data: payload,
64
+ };
65
+ }
66
+ export const trainingInsightsToolDefinitions = [
67
+ {
68
+ name: "get_training_insights",
69
+ description: "Returns a combined weekly training summary: latest activity, recent workouts, sleep, recovery, and stress.",
70
+ inputSchema: {
71
+ days: {
72
+ type: "number",
73
+ description: "Number of days to summarize. Defaults to 7.",
74
+ },
75
+ },
76
+ handler: getTrainingInsights,
77
+ },
78
+ ];
79
+ //# sourceMappingURL=trainingInsights.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trainingInsights.js","sourceRoot":"","sources":["../../src/tools/trainingInsights.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { ToolResult } from "../garmin/types.js";
2
+ export interface ToolInputSchema {
3
+ type: string;
4
+ description: string;
5
+ }
6
+ export interface ToolDefinition {
7
+ name: string;
8
+ description: string;
9
+ inputSchema: Record<string, ToolInputSchema> | Record<string, never>;
10
+ handler: (input: Record<string, unknown>) => Promise<ToolResult<unknown>>;
11
+ }
12
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACrE,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;CAC3E"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ import { type Vo2MaxEntry } from "../garmin/rawApi.js";
2
+ import type { ToolResult } from "../garmin/types.js";
3
+ import type { Vo2MaxPayload, StoredProvenance } from "./payloads.js";
4
+ import type { ToolDefinition } from "./types.js";
5
+ export declare function buildVo2MaxPayload(entries: Vo2MaxEntry[], requestedDays: number, unreachableDays?: number, stored?: StoredProvenance): Vo2MaxPayload;
6
+ export declare function renderVo2MaxText(payload: Vo2MaxPayload): string;
7
+ export declare function getVo2MaxTrends(input: {
8
+ days?: number;
9
+ }): Promise<ToolResult<Vo2MaxPayload>>;
10
+ export declare const vo2MaxToolDefinitions: ToolDefinition[];
11
+ //# sourceMappingURL=vo2Max.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vo2Max.d.ts","sourceRoot":"","sources":["../../src/tools/vo2Max.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA4CjD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,WAAW,EAAE,EACtB,aAAa,EAAE,MAAM,EACrB,eAAe,SAAI,EACnB,MAAM,GAAE,gBAIP,GACA,aAAa,CAiBf;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAsC/D;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACvB,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAkBpC;AAED,eAAO,MAAM,qBAAqB,EAAE,cAAc,EAYjD,CAAC"}
@@ -0,0 +1,110 @@
1
+ import { appConfig } from "../config.js";
2
+ import { buildToolCacheKey, withCache } from "../garmin/cache.js";
3
+ import { withGarminClient } from "../garmin/client.js";
4
+ import { fetchVo2MaxDay } from "../garmin/daily.js";
5
+ import { mapMaxMetrics } from "../garmin/rawApi.js";
6
+ import { fetchEachDay } from "../garmin/partial.js";
7
+ import { fetchDaysOrStore, isPartialOrStored, storedFetchNote, } from "../history/fallback.js";
8
+ import { calculateTrend, getDateRange } from "../utils/helpers.js";
9
+ async function fetchVo2MaxDays(days) {
10
+ const dates = getDateRange(days);
11
+ // `dateOf` returns the date the reading was TAKEN, not the date asked about:
12
+ // `maxmet/latest/<date>` ignores its date and answers with the current
13
+ // measurement, so the same reading arrives once per day requested. Keying the
14
+ // merge on the measured date is what collapses those duplicates -- and it
15
+ // does the same job for a stored entry, which is why this tool needs no
16
+ // dedupe pass of its own any more.
17
+ return fetchDaysOrStore({
18
+ dates,
19
+ source: "vo2max",
20
+ live: () => withGarminClient(async (client) => fetchEachDay(dates, async (date) => (await fetchVo2MaxDay(client, date)).mapped, "vo2max")),
21
+ fromRaw: (date, payload) => mapMaxMetrics(date, payload),
22
+ fromMetrics: (date, metrics) => {
23
+ const vo2Max = metrics.get("vo2max") ?? null;
24
+ const vo2MaxCycling = metrics.get("vo2max_cycling") ?? null;
25
+ if (vo2Max === null && vo2MaxCycling === null) {
26
+ return null;
27
+ }
28
+ return { date, vo2Max, vo2MaxCycling };
29
+ },
30
+ dateOf: (entry) => entry.date,
31
+ });
32
+ }
33
+ export function buildVo2MaxPayload(entries, requestedDays, unreachableDays = 0, stored = {
34
+ storedDays: 0,
35
+ storedThrough: null,
36
+ storedWindowMoved: false,
37
+ }) {
38
+ const values = entries
39
+ .map((entry) => entry.vo2Max)
40
+ .filter((value) => value !== null);
41
+ return {
42
+ requestedDays,
43
+ recordedDays: entries.length,
44
+ unreachableDays,
45
+ current: entries[0]?.vo2Max ?? null,
46
+ oldest: entries.at(-1)?.vo2Max ?? null,
47
+ trend: calculateTrend(values, false),
48
+ entries,
49
+ storedDays: stored.storedDays,
50
+ storedThrough: stored.storedThrough,
51
+ storedWindowMoved: stored.storedWindowMoved,
52
+ };
53
+ }
54
+ export function renderVo2MaxText(payload) {
55
+ const note = storedFetchNote({
56
+ values: payload.entries,
57
+ unreachableDays: payload.unreachableDays,
58
+ requestedDays: payload.requestedDays,
59
+ storedDays: payload.storedDays,
60
+ storedThrough: payload.storedThrough,
61
+ storedWindowMoved: payload.storedWindowMoved,
62
+ }, "days");
63
+ if (payload.recordedDays === 0) {
64
+ return note || `No VO2 max data found for the last ${payload.requestedDays} days.`;
65
+ }
66
+ const measuredEntries = payload.entries.filter((entry) => entry.vo2Max !== null).length;
67
+ const lines = payload.entries.slice(0, 10).map((entry) => {
68
+ const cycling = entry.vo2MaxCycling !== null ? `, cycling ${entry.vo2MaxCycling}` : "";
69
+ return `${entry.date}: VO2 max ${entry.vo2Max ?? "n/a"}${cycling}`;
70
+ });
71
+ return [
72
+ note,
73
+ `VO2 max trends over ${payload.recordedDays} recorded days:`,
74
+ payload.current !== null ? `Current VO2 max: ${payload.current}` : "",
75
+ payload.oldest !== null && payload.recordedDays > 1
76
+ ? `Oldest in range: ${payload.oldest}`
77
+ : "",
78
+ measuredEntries > 1 ? `Trend: ${payload.trend}` : "",
79
+ "",
80
+ "Recent entries:",
81
+ ...lines,
82
+ ]
83
+ .filter(Boolean)
84
+ .join("\n");
85
+ }
86
+ export async function getVo2MaxTrends(input) {
87
+ const days = input.days ?? 30;
88
+ const cacheKey = buildToolCacheKey("get_vo2_max_trends", { days });
89
+ const fetched = await withCache(cacheKey, appConfig.cacheTtlStats, async () => fetchVo2MaxDays(days), { isPartial: isPartialOrStored });
90
+ const payload = buildVo2MaxPayload(fetched.values, days, fetched.unreachableDays, fetched);
91
+ return {
92
+ type: "text",
93
+ text: renderVo2MaxText(payload),
94
+ data: payload,
95
+ };
96
+ }
97
+ export const vo2MaxToolDefinitions = [
98
+ {
99
+ name: "get_vo2_max_trends",
100
+ description: "Returns VO2 max fitness trends over time from Garmin Connect.",
101
+ inputSchema: {
102
+ days: {
103
+ type: "number",
104
+ description: "Number of days to analyze. Defaults to 30.",
105
+ },
106
+ },
107
+ handler: getVo2MaxTrends,
108
+ },
109
+ ];
110
+ //# sourceMappingURL=vo2Max.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vo2Max.js","sourceRoot":"","sources":["../../src/tools/vo2Max.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { type WeekReview } from "../detect/week.js";
2
+ import { type RaceCountdown } from "../detect/countdown.js";
3
+ import type { ToolResult } from "../garmin/types.js";
4
+ import type { ToolDefinition } from "./types.js";
5
+ export interface WeekPayload {
6
+ review: WeekReview;
7
+ race: RaceCountdown | null;
8
+ }
9
+ export declare function renderWeekText(payload: WeekPayload): string;
10
+ export declare function getWeekReview(): Promise<ToolResult<WeekPayload>>;
11
+ export declare const weekToolDefinitions: ToolDefinition[];
12
+ //# sourceMappingURL=week.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"week.d.ts","sourceRoot":"","sources":["../../src/tools/week.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAA0B,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEpF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAcjD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,aAAa,GAAG,IAAI,CAAC;CAC5B;AAmBD,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CA+C3D;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAYtE;AAED,eAAO,MAAM,mBAAmB,EAAE,cAAc,EAQ/C,CAAC"}
@@ -0,0 +1,74 @@
1
+ import { DateTime } from "luxon";
2
+ import { buildDetectorInput } from "../detect/index.js";
3
+ import { buildWeekReview } from "../detect/week.js";
4
+ import { nextRace, describeRace } from "../detect/countdown.js";
5
+ import { activeContext, upcomingContext } from "../history/context.js";
6
+ function formatMetric(metric) {
7
+ if (metric.current === null) {
8
+ return `${metric.label}: not recorded this week`;
9
+ }
10
+ const unit = metric.unit ? metric.unit : "";
11
+ const current = `${metric.current}${unit}`;
12
+ if (metric.previous === null) {
13
+ return `${metric.label}: ${current} (no comparable week before it)`;
14
+ }
15
+ const delta = metric.delta ?? 0;
16
+ const sign = delta > 0 ? "+" : "";
17
+ return `${metric.label}: ${current} vs ${metric.previous}${unit} (${sign}${delta}${unit})`;
18
+ }
19
+ export function renderWeekText(payload) {
20
+ const { review, race } = payload;
21
+ if (!review.ready) {
22
+ return [
23
+ review.headline,
24
+ "Run `trainbud backfill` to pull what Garmin already holds.",
25
+ ].join("\n");
26
+ }
27
+ const lines = [
28
+ `Week of ${review.start} to ${review.end}`,
29
+ "",
30
+ review.headline,
31
+ "",
32
+ `Sessions: ${review.sessions} (${review.movingMinutes} min) vs ${review.previousSessions} (${review.previousMovingMinutes} min) last week`,
33
+ "",
34
+ ...review.metrics.map(formatMetric),
35
+ ];
36
+ if (review.forecast.verdict !== "unknown") {
37
+ lines.push("", `Next week: ${review.forecast.summary}`);
38
+ if (review.forecast.weeklyLoadAdjustment !== null) {
39
+ const adjustment = review.forecast.weeklyLoadAdjustment;
40
+ lines.push(adjustment < 0
41
+ ? `To stay inside the band, this week's load would need to come down by about ${Math.abs(adjustment)} TRIMP.`
42
+ : `To stay inside the band, this week's load would need to come up by about ${adjustment} TRIMP.`);
43
+ }
44
+ }
45
+ if (review.sleep.habitualHours !== null) {
46
+ lines.push("", `Sleep: ${review.sleep.summary}`);
47
+ }
48
+ const raceLine = describeRace(race);
49
+ if (raceLine) {
50
+ lines.push("", raceLine);
51
+ }
52
+ lines.push("", "These are measurements against the user's own baselines, not diagnoses.");
53
+ return lines.join("\n");
54
+ }
55
+ export async function getWeekReview() {
56
+ const today = DateTime.local().toISODate() ?? "";
57
+ const review = buildWeekReview(buildDetectorInput());
58
+ const race = nextRace([...activeContext(today), ...upcomingContext(today)], today);
59
+ const payload = { review, race };
60
+ return {
61
+ type: "text",
62
+ text: renderWeekText(payload),
63
+ data: payload,
64
+ };
65
+ }
66
+ export const weekToolDefinitions = [
67
+ {
68
+ name: "get_week_review",
69
+ description: "Returns this training week against the previous one — sessions, training load, sleep, resting heart rate, HRV and stress — plus where the acute:chronic load ratio lands if next week repeats this one, a sleep debt and consistency read against the user's own baseline, and the next race on record. Prefer this over reading individual metrics when asked how the week or the block is going, whether to back off, or what to do next week.",
70
+ inputSchema: {},
71
+ handler: getWeekReview,
72
+ },
73
+ ];
74
+ //# sourceMappingURL=week.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"week.js","sourceRoot":"","sources":["../../src/tools/week.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ export type AiUsageKind = "ask" | "insight";
2
+ export type UsageSource = "watch" | "dashboard" | "cli" | "mcp" | "server";
3
+ export interface TokenCounts {
4
+ inputTokens: number;
5
+ outputTokens: number;
6
+ cacheReadTokens?: number;
7
+ cacheWriteTokens?: number;
8
+ }
9
+ export interface AiUsageInput extends TokenCounts {
10
+ kind: AiUsageKind;
11
+ model: string;
12
+ source: UsageSource;
13
+ /** Unix seconds. Defaults to now; present so tests can place a call in time. */
14
+ at?: number;
15
+ }
16
+ export interface AiUsageRow extends AiUsageInput {
17
+ at: number;
18
+ costUsd: number | null;
19
+ }
20
+ export interface AiSpend {
21
+ calls: number;
22
+ inputTokens: number;
23
+ outputTokens: number;
24
+ costUsd: number;
25
+ /** Calls whose model had no published price here. */
26
+ unpricedCalls: number;
27
+ }
28
+ export interface BudgetState {
29
+ capUsd: number | null;
30
+ spentUsd: number;
31
+ remainingUsd: number | null;
32
+ exceeded: boolean;
33
+ /** True when unpriced calls make `spentUsd` a floor rather than a total. */
34
+ incomplete: boolean;
35
+ }
36
+ /** Cost in USD, or null when this model has no price here. */
37
+ export declare function priceOf(model: string, tokens: TokenCounts): number | null;
38
+ export declare function recordAiUsage(input: AiUsageInput): AiUsageRow;
39
+ export declare function aiSpendSince(sinceUnixSeconds: number): AiSpend;
40
+ /**
41
+ * Spend since the first of the current month, in the user's own local time.
42
+ *
43
+ * A rolling thirty days would be a different and worse number: a monthly cap
44
+ * that resets on a sliding window never resets, and the user compares this
45
+ * against a bill that runs calendar months.
46
+ */
47
+ export declare function monthToDateSpend(): AiSpend;
48
+ export declare function budgetState(): BudgetState;
49
+ /** Thrown when a request is refused because the user's own cap is reached. */
50
+ export declare class BudgetExceededError extends Error {
51
+ constructor(state: BudgetState);
52
+ }
53
+ /** Throws when a cap is set and already reached. No cap means never throws. */
54
+ export declare function assertWithinBudget(): void;
55
+ export interface AiUsageEntry {
56
+ at: number;
57
+ kind: string;
58
+ model: string;
59
+ source: string;
60
+ inputTokens: number;
61
+ outputTokens: number;
62
+ costUsd: number | null;
63
+ }
64
+ /** The most recent calls, newest first — the ledger behind the total. */
65
+ export declare function recentAiUsage(limit?: number): AiUsageEntry[];
66
+ export interface DailySpend {
67
+ day: string;
68
+ costUsd: number;
69
+ calls: number;
70
+ }
71
+ /**
72
+ * Cost per day for the last `days` days, including the days that cost nothing.
73
+ *
74
+ * The zero days are the point. A chart built only from rows that exist draws a
75
+ * continuous line through a week the user never opened the app, which reads as
76
+ * steady daily spend rather than as the four days it actually was.
77
+ */
78
+ export declare function dailyAiSpend(days?: number): DailySpend[];
79
+ export interface FeatureCount {
80
+ name: string;
81
+ count: number;
82
+ }
83
+ /**
84
+ * Count one use of one feature, today.
85
+ *
86
+ * Everything about this is best-effort. It is called from render paths and
87
+ * request handlers that have real work to do, and a counter that can throw is
88
+ * a counter that takes down the feature it was measuring.
89
+ */
90
+ export declare function recordFeature(name: string): void;
91
+ export declare function featureCounts(days?: number): FeatureCount[];
92
+ /**
93
+ * Delete every stored feature counter.
94
+ *
95
+ * Offered because "stop counting" and "forget what you counted" are different
96
+ * requests, and a privacy control that only does the first is the kind that
97
+ * gets described as a privacy control and is not one. AI spend is deliberately
98
+ * NOT cleared here: that is the user's own billing record against a cap they
99
+ * set, and losing it would make the cap wrong for the rest of the month.
100
+ */
101
+ export declare function clearFeatureUsage(): void;
102
+ /** Tests only. */
103
+ export declare function __clearUsageForTests(): void;
104
+ //# sourceMappingURL=usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"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"}