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,225 @@
1
+ import { appConfig, watchSetupReadError } from "./config.js";
2
+ import { isAiConfigured } from "./promptApi.js";
3
+ import { getPendingPairings } from "./pairApi.js";
4
+ import { runDetectors } from "./detect/index.js";
5
+ /**
6
+ * Connect IQ sends `Mozilla/5.0` and does not let an app override it, which is
7
+ * why ngrok's free tier used to answer the watch with an HTML interstitial
8
+ * while curl got clean JSON. Impersonating the watch exactly -- browser UA,
9
+ * the skip header, JSON expected -- is the entire point: a probe that is
10
+ * treated better than the real client tests nothing.
11
+ */
12
+ const WATCH_HEADERS = {
13
+ "User-Agent": "Mozilla/5.0",
14
+ "ngrok-skip-browser-warning": "1",
15
+ };
16
+ const PROBE_TIMEOUT_MS = 10_000;
17
+ function classify(status) {
18
+ if (status === 401 || status === 403)
19
+ return "unauthorized";
20
+ if (status === 429)
21
+ return "refused";
22
+ if (status >= 500)
23
+ return "unreachable";
24
+ return status === 200 ? "ok" : "not_server";
25
+ }
26
+ /**
27
+ * Fetches `${baseUrl}/health` the way the watch would.
28
+ *
29
+ * Injectable fetch so the tests never touch the network -- the previous
30
+ * generation of tests in this project reached real services and a real
31
+ * database, and both cost a debugging session.
32
+ */
33
+ export async function probePublicUrl(baseUrl, fetchImpl = fetch) {
34
+ const url = `${baseUrl.replace(/\/$/, "")}/health`;
35
+ const controller = new AbortController();
36
+ const timer = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
37
+ try {
38
+ const response = await fetchImpl(url, {
39
+ headers: WATCH_HEADERS,
40
+ signal: controller.signal,
41
+ });
42
+ const contentType = response.headers.get("content-type");
43
+ const text = await response.text();
44
+ const bodyStart = text.slice(0, 200);
45
+ let reach = classify(response.status);
46
+ // A 200 is not enough. The store bug and the pairing bug were both a 200 or
47
+ // a 404 carrying HTML, and the watch's failure is a PARSE failure, not a
48
+ // status failure -- so grade the body, not the code.
49
+ if (reach === "ok") {
50
+ try {
51
+ const parsed = JSON.parse(text);
52
+ if (parsed.service !== "trainbud") {
53
+ reach = "not_server";
54
+ }
55
+ }
56
+ catch {
57
+ reach = "not_server";
58
+ }
59
+ }
60
+ return { reach, status: response.status, contentType, bodyStart };
61
+ }
62
+ catch (error) {
63
+ return {
64
+ reach: "unreachable",
65
+ status: null,
66
+ contentType: null,
67
+ bodyStart: "",
68
+ error: error instanceof Error ? error.message : String(error),
69
+ };
70
+ }
71
+ finally {
72
+ clearTimeout(timer);
73
+ }
74
+ }
75
+ function describeReach(outcome, url) {
76
+ switch (outcome.reach) {
77
+ case "ok":
78
+ return {
79
+ name: "Watch can reach this server",
80
+ ok: true,
81
+ detail: `${url}/health answered with TrainBud's own JSON.`,
82
+ };
83
+ case "not_server":
84
+ return {
85
+ name: "Watch can reach this server",
86
+ ok: false,
87
+ detail: outcome.status === null
88
+ ? "Something answered, but not with TrainBud's JSON."
89
+ : `${url}/health answered ${outcome.status} with ${outcome.contentType ?? "an unknown content type"}, not TrainBud's JSON. The watch cannot parse this and will report error -400 "Not a TrainBud server". First bytes: ${outcome.bodyStart.slice(0, 120)}`,
90
+ fix: "Your tunnel is answering instead of TrainBud. Restart it, or point Server URL at a tunnel that is actually forwarding to this machine.",
91
+ };
92
+ case "unauthorized":
93
+ return {
94
+ name: "Watch can reach this server",
95
+ ok: false,
96
+ detail: `${url}/health answered ${outcome.status}. Something in front of this server is asking for its own credentials.`,
97
+ fix: "Remove the authentication in front of the tunnel; TrainBud does its own.",
98
+ };
99
+ case "refused":
100
+ return {
101
+ name: "Watch can reach this server",
102
+ ok: false,
103
+ detail: `${url}/health answered 429. Something is rate limiting the watch.`,
104
+ fix: "Wait a minute, then run this again.",
105
+ };
106
+ default:
107
+ return {
108
+ name: "Watch can reach this server",
109
+ ok: false,
110
+ detail: `${url}/health could not be reached: ${outcome.error ?? "no response"}.`,
111
+ fix: "Start your tunnel, and check that it forwards to this machine's port.",
112
+ };
113
+ }
114
+ }
115
+ /**
116
+ * Fourteen days is the threshold every detector uses before it will compare a
117
+ * day against a baseline. Depth alone is not the whole test, and reading only
118
+ * `days` here reproduced the exact bug `ready` was added to prevent: a store
119
+ * holding 74 days that stops on 08-21 answered "enough to compare a day against
120
+ * your own baselines" on 09-03. Every detector then correctly found nothing --
121
+ * because there was nothing recent to find -- and the one command whose job is
122
+ * to say what is wrong said everything was fine.
123
+ *
124
+ * Extracted so this can be tested without standing up a history database; the
125
+ * version that shipped the bug was only reachable through one.
126
+ */
127
+ export function describeHistoryCoverage(coverage) {
128
+ const stale = coverage.days >= 14 && !coverage.ready;
129
+ return {
130
+ name: "History depth",
131
+ ok: coverage.ready,
132
+ warning: stale || (coverage.days > 0 && coverage.days < 14),
133
+ detail: coverage.ready
134
+ ? `${coverage.days} days stored through ${coverage.throughDate ?? "today"}, enough to compare a day against your own baselines.`
135
+ : stale
136
+ ? `${coverage.days} days stored, but the record stops at ${coverage.throughDate ?? "an unknown date"} — ${coverage.staleDays} days ago. Findings describe days you have already lived past.`
137
+ : `${coverage.days} days stored; 14 are needed before anything can be compared against a baseline.`,
138
+ fix: coverage.ready
139
+ ? undefined
140
+ : stale
141
+ ? "Run `trainbud backfill --days 20` to catch the record up."
142
+ : "Run `trainbud backfill --days 365`.",
143
+ };
144
+ }
145
+ export async function runSelfTest(fetchImpl = fetch) {
146
+ const checks = [];
147
+ const publicUrl = appConfig.publicUrl;
148
+ let reach = "not_configured";
149
+ if (!publicUrl) {
150
+ // A file that exists and will not parse is a different fault from no file
151
+ // at all, and reporting both as "not configured" sent the reader to set an
152
+ // environment variable that was never the problem.
153
+ const readError = watchSetupReadError();
154
+ checks.push(readError === null
155
+ ? {
156
+ name: "Public URL",
157
+ ok: false,
158
+ detail: "No public URL is configured, so the watch has no address to call.",
159
+ fix: "Set TRAINBUD_PUBLIC_URL in .env, or pair a watch once so the address is recorded.",
160
+ }
161
+ : {
162
+ name: "Public URL",
163
+ ok: false,
164
+ detail: `The watch setup file exists but could not be read: ${readError}`,
165
+ fix: "Rewrite .trainbud/watch-setup.json as UTF-8 with no byte order mark, or re-run scripts/start-watch-stack.ps1.",
166
+ });
167
+ }
168
+ else {
169
+ checks.push({
170
+ name: "Public URL",
171
+ ok: true,
172
+ detail: publicUrl,
173
+ });
174
+ const outcome = await probePublicUrl(publicUrl, fetchImpl);
175
+ reach = outcome.reach;
176
+ checks.push(describeReach(outcome, publicUrl));
177
+ if (!publicUrl.startsWith("https://")) {
178
+ checks.push({
179
+ name: "HTTPS",
180
+ ok: false,
181
+ detail: "The public URL is not https.",
182
+ fix: "Connect IQ refuses plain http outright, with error -1001. Use an https URL.",
183
+ });
184
+ }
185
+ }
186
+ const aiReady = isAiConfigured();
187
+ checks.push({
188
+ name: "AI features",
189
+ ok: aiReady,
190
+ warning: !aiReady,
191
+ detail: aiReady
192
+ ? "An Anthropic key is configured, so the Ask card and the daily insight can run."
193
+ : "No Anthropic key. AI is bring-your-own-key, so the Ask card and the daily insight cannot run.",
194
+ fix: aiReady ? undefined : "Paste a key into the AI section of this dashboard.",
195
+ });
196
+ // The detectors' own coverage figure, not a row count: it is the number the
197
+ // 14-day threshold is actually applied to, so the dashboard and the watch
198
+ // cannot disagree about whether there is enough history.
199
+ let coverage;
200
+ try {
201
+ coverage = runDetectors().coverage;
202
+ }
203
+ catch {
204
+ // A store that cannot be opened is a real answer here: zero days known.
205
+ coverage = { days: 0, ready: false, throughDate: null, staleDays: 0 };
206
+ }
207
+ checks.push(describeHistoryCoverage(coverage));
208
+ const pending = getPendingPairings().length;
209
+ if (pending > 0) {
210
+ checks.push({
211
+ name: "Pairing",
212
+ ok: true,
213
+ warning: true,
214
+ detail: `${pending} pairing code(s) waiting for approval.`,
215
+ fix: "Approve the code shown on your watch in the Pairing section.",
216
+ });
217
+ }
218
+ return {
219
+ reach,
220
+ publicUrl,
221
+ checks,
222
+ ok: checks.every((check) => check.ok),
223
+ };
224
+ }
225
+ //# sourceMappingURL=selfTest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selfTest.js","sourceRoot":"","sources":["../src/selfTest.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: "trainbud",
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("TrainBud 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,UAAU;QAChB,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,4CAA4C,CAAC,CAAC;QAC5D,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,CAuF9C"}
package/dist/setup.js ADDED
@@ -0,0 +1,210 @@
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 { configureTrainBudForClient, 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("\nTrainBud setup\n");
60
+ output.write("This wizard will save credentials locally, authenticate with Garmin Connect,\n");
61
+ output.write("and optionally connect TrainBud 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 TrainBud 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 diagnostics now (config + live API)? [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 `trainbud 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
+ configureTrainBudForClient(client, {
163
+ email: credentials.email,
164
+ password: credentials.password,
165
+ distIndexPath,
166
+ });
167
+ output.write(`Configured TrainBud 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 TRAINBUD_API_KEY\n`);
172
+ output.write(`- Run: trainbud 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
+ // The watch app is the part people get stuck on: it needs a public HTTPS URL
176
+ // and a pairing approval, neither of which the wizard used to mention.
177
+ const publicUrl = appConfig.publicUrl;
178
+ const dashboardBase = publicUrl || `http://${appConfig.mcpHost}:${appConfig.mcpPort}`;
179
+ output.write("\nWatch app (Connect IQ):\n");
180
+ if (publicUrl) {
181
+ output.write(`- Public URL: ${publicUrl}\n`);
182
+ }
183
+ else {
184
+ output.write("- No public URL yet. Run `scripts/start-watch-stack.ps1`, or set TRAINBUD_PUBLIC_URL\n");
185
+ output.write(" in .env to an HTTPS tunnel pointing at this server. The watch needs HTTPS.\n");
186
+ }
187
+ output.write("- Enter that URL as Server URL in the Connect IQ app settings for TrainBud\n");
188
+ output.write("- Open the app on the watch, then approve the 6-digit code at:\n");
189
+ output.write(` ${dashboardBase}/dashboard?token=${appConfig.mcpApiKey}\n`);
190
+ output.write("- AI cards stay empty until you add an AI provider key on that dashboard\n");
191
+ if (selectedClients.length > 0) {
192
+ output.write("\nNext steps:\n");
193
+ output.write("- Restart your MCP client completely so it picks up the new server\n");
194
+ output.write("- Ask things like \"What was my last workout?\" or \"How did I sleep this week?\"\n");
195
+ }
196
+ else {
197
+ output.write("\nNext steps:\n");
198
+ output.write("- Run `trainbud start` for manual MCP usage, or follow QUICKSTART.md to connect a client\n");
199
+ }
200
+ if (await promptRunLiveCheck()) {
201
+ assertGarminCredentials();
202
+ const results = await runLiveCheck();
203
+ output.write("\n");
204
+ printLiveCheckResults(results);
205
+ if (results.some((result) => !result.ok)) {
206
+ process.exitCode = 1;
207
+ }
208
+ }
209
+ }
210
+ //# 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 "trainbud 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,0CAA0C,CAAC;QAC1F,CAAC;QAED,OAAO,oBAAoB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,+DAA+D,CAAC;AACzE,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { ActivitySummary, ToolResult } from "../garmin/types.js";
2
+ import type { ActivitiesRangePayload, LatestActivityPayload } from "./payloads.js";
3
+ import type { ToolDefinition } from "./types.js";
4
+ export declare function formatActivitySummary(activity: ActivitySummary, units?: "metric" | "imperial"): string;
5
+ export interface ActivityPool {
6
+ activities: ActivitySummary[];
7
+ truncated: boolean;
8
+ /** True when this came out of the local store rather than off the wire. */
9
+ fromStore: boolean;
10
+ }
11
+ export declare function getActivitiesPool(): Promise<ActivityPool>;
12
+ export declare function renderLatestActivityText(payload: LatestActivityPayload, units?: "metric" | "imperial"): string;
13
+ export declare function getLatestActivity(): Promise<ToolResult<LatestActivityPayload>>;
14
+ export declare function buildActivitiesRangePayload(activities: ActivitySummary[], startDate: string, endDate: string, truncated: boolean, fromStore?: boolean): ActivitiesRangePayload;
15
+ export declare function renderActivitiesRangeText(payload: ActivitiesRangePayload, units?: "metric" | "imperial"): string;
16
+ export declare function getActivitiesRange(input: Record<string, unknown>): Promise<ToolResult<ActivitiesRangePayload>>;
17
+ export declare const activityToolDefinitions: ToolDefinition[];
18
+ //# 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,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACnF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAoBjD,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,eAAe,EACzB,KAAK,GAAE,QAAQ,GAAG,UAAqB,GACtC,MAAM,CAaR;AAyCD,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAC;CACpB;AA2BD,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,YAAY,CAAC,CAQ/D;AAID,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,qBAAqB,EAC9B,KAAK,GAAE,QAAQ,GAAG,UAAqB,GACtC,MAAM,CAiBR;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC,CAoBpF;AAED,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,eAAe,EAAE,EAC7B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,OAAO,EAClB,SAAS,UAAQ,GAChB,sBAAsB,CAExB;AAED,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,sBAAsB,EAC/B,KAAK,GAAE,QAAQ,GAAG,UAAqB,GACtC,MAAM,CAoCR;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC,CAkB7C;AAED,eAAO,MAAM,uBAAuB,EAAE,cAAc,EAsBnD,CAAC"}