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.
- package/.env.example +11 -0
- package/CHANGELOG.md +56 -0
- package/LICENSE +21 -0
- package/QUICKSTART.md +174 -0
- package/README.md +227 -0
- package/dist/appDb.d.ts +32 -0
- package/dist/appDb.d.ts.map +1 -0
- package/dist/appDb.js +131 -0
- package/dist/appDb.js.map +1 -0
- package/dist/check.d.ts +9 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +156 -0
- package/dist/check.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +187 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +28 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +108 -0
- package/dist/config.js.map +1 -0
- package/dist/dashboard.d.ts +4 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +111 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/garmin/auth.d.ts +10 -0
- package/dist/garmin/auth.d.ts.map +1 -0
- package/dist/garmin/auth.js +81 -0
- package/dist/garmin/auth.js.map +1 -0
- package/dist/garmin/cache.d.ts +20 -0
- package/dist/garmin/cache.d.ts.map +1 -0
- package/dist/garmin/cache.js +115 -0
- package/dist/garmin/cache.js.map +1 -0
- package/dist/garmin/client.d.ts +5 -0
- package/dist/garmin/client.d.ts.map +1 -0
- package/dist/garmin/client.js +68 -0
- package/dist/garmin/client.js.map +1 -0
- package/dist/garmin/garminApiTypes.d.ts +70 -0
- package/dist/garmin/garminApiTypes.d.ts.map +1 -0
- package/dist/garmin/garminApiTypes.js +3 -0
- package/dist/garmin/garminApiTypes.js.map +1 -0
- package/dist/garmin/garminConnect.d.ts +17 -0
- package/dist/garmin/garminConnect.d.ts.map +1 -0
- package/dist/garmin/garminConnect.js +5 -0
- package/dist/garmin/garminConnect.js.map +1 -0
- package/dist/garmin/rawApi.d.ts +18 -0
- package/dist/garmin/rawApi.d.ts.map +1 -0
- package/dist/garmin/rawApi.js +53 -0
- package/dist/garmin/rawApi.js.map +1 -0
- package/dist/garmin/types.d.ts +68 -0
- package/dist/garmin/types.d.ts.map +1 -0
- package/dist/garmin/types.js +11 -0
- package/dist/garmin/types.js.map +1 -0
- package/dist/httpServer.d.ts +7 -0
- package/dist/httpServer.d.ts.map +1 -0
- package/dist/httpServer.js +386 -0
- package/dist/httpServer.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/mcpConfig.d.ts +37 -0
- package/dist/mcpConfig.d.ts.map +1 -0
- package/dist/mcpConfig.js +83 -0
- package/dist/mcpConfig.js.map +1 -0
- package/dist/pairApi.d.ts +14 -0
- package/dist/pairApi.d.ts.map +1 -0
- package/dist/pairApi.js +34 -0
- package/dist/pairApi.js.map +1 -0
- package/dist/promptApi.d.ts +13 -0
- package/dist/promptApi.d.ts.map +1 -0
- package/dist/promptApi.js +102 -0
- package/dist/promptApi.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +67 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +2 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +194 -0
- package/dist/setup.js.map +1 -0
- package/dist/toolErrors.d.ts +2 -0
- package/dist/toolErrors.d.ts.map +1 -0
- package/dist/toolErrors.js +18 -0
- package/dist/toolErrors.js.map +1 -0
- package/dist/tools/activities.d.ts +11 -0
- package/dist/tools/activities.d.ts.map +1 -0
- package/dist/tools/activities.js +134 -0
- package/dist/tools/activities.js.map +1 -0
- package/dist/tools/bodyComposition.d.ts +7 -0
- package/dist/tools/bodyComposition.d.ts.map +1 -0
- package/dist/tools/bodyComposition.js +97 -0
- package/dist/tools/bodyComposition.js.map +1 -0
- package/dist/tools/heartRate.d.ts +7 -0
- package/dist/tools/heartRate.d.ts.map +1 -0
- package/dist/tools/heartRate.js +88 -0
- package/dist/tools/heartRate.js.map +1 -0
- package/dist/tools/index.d.ts +39 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +77 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/recovery.d.ts +16 -0
- package/dist/tools/recovery.d.ts.map +1 -0
- package/dist/tools/recovery.js +214 -0
- package/dist/tools/recovery.js.map +1 -0
- package/dist/tools/sleep.d.ts +7 -0
- package/dist/tools/sleep.d.ts.map +1 -0
- package/dist/tools/sleep.js +93 -0
- package/dist/tools/sleep.js.map +1 -0
- package/dist/tools/stress.d.ts +7 -0
- package/dist/tools/stress.d.ts.map +1 -0
- package/dist/tools/stress.js +69 -0
- package/dist/tools/stress.js.map +1 -0
- package/dist/tools/trainingInsights.d.ts +7 -0
- package/dist/tools/trainingInsights.d.ts.map +1 -0
- package/dist/tools/trainingInsights.js +61 -0
- package/dist/tools/trainingInsights.js.map +1 -0
- package/dist/tools/types.d.ts +12 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/vo2Max.d.ts +7 -0
- package/dist/tools/vo2Max.d.ts.map +1 -0
- package/dist/tools/vo2Max.js +72 -0
- package/dist/tools/vo2Max.js.map +1 -0
- package/dist/utils/batch.d.ts +2 -0
- package/dist/utils/batch.d.ts.map +1 -0
- package/dist/utils/batch.js +18 -0
- package/dist/utils/batch.js.map +1 -0
- package/dist/utils/helpers.d.ts +19 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +131 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +59 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +7 -0
- package/dist/version.js.map +1 -0
- package/dist/watchApi.d.ts +47 -0
- package/dist/watchApi.d.ts.map +1 -0
- package/dist/watchApi.js +222 -0
- package/dist/watchApi.js.map +1 -0
- package/package.json +77 -0
package/dist/check.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { DateTime } from "luxon";
|
|
2
|
+
import { assertGarminCredentials, appConfig } from "./config.js";
|
|
3
|
+
import { executeTool, listRegisteredToolNames } from "./tools/index.js";
|
|
4
|
+
import { configureLogger } from "./utils/logger.js";
|
|
5
|
+
function buildDefaultToolChecks() {
|
|
6
|
+
const endDate = DateTime.now().toISODate();
|
|
7
|
+
const startDate = DateTime.now().minus({ days: 30 }).toISODate();
|
|
8
|
+
return [
|
|
9
|
+
{ name: "get_latest_activity", args: {} },
|
|
10
|
+
{
|
|
11
|
+
name: "get_activities_range",
|
|
12
|
+
args: {
|
|
13
|
+
start_date: startDate,
|
|
14
|
+
end_date: endDate,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
{ name: "get_sleep_data", args: { nights: 7 } },
|
|
18
|
+
{ name: "get_heart_rate_trends", args: { days: 30 } },
|
|
19
|
+
{ name: "get_recovery_status", args: {} },
|
|
20
|
+
{ name: "get_body_composition", args: { days: 30 } },
|
|
21
|
+
{ name: "get_stress_levels", args: { days: 7 } },
|
|
22
|
+
{ name: "get_vo2_max_trends", args: { days: 30 } },
|
|
23
|
+
{ name: "get_training_insights", args: { days: 7 } },
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
function summarizeToolResult(name, text) {
|
|
27
|
+
const normalized = text.trim();
|
|
28
|
+
const noDataPattern = /^No .+ found/i;
|
|
29
|
+
if (noDataPattern.test(normalized)) {
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
summary: normalized.split("\n")[0] ?? normalized,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (name === "get_latest_activity") {
|
|
36
|
+
const activityLine = normalized.split("\n").find((line) => line.startsWith("Activity:"));
|
|
37
|
+
const dateLine = normalized.split("\n").find((line) => line.startsWith("Date:"));
|
|
38
|
+
const distanceLine = normalized.split("\n").find((line) => line.startsWith("Distance:"));
|
|
39
|
+
const parts = [activityLine, distanceLine, dateLine].filter(Boolean);
|
|
40
|
+
return {
|
|
41
|
+
ok: true,
|
|
42
|
+
summary: parts.length > 0 ? parts.join(", ") : "Latest activity retrieved",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (name === "get_activities_range") {
|
|
46
|
+
const countMatch = normalized.match(/(\d+) activit/i);
|
|
47
|
+
return {
|
|
48
|
+
ok: true,
|
|
49
|
+
summary: countMatch ? `${countMatch[1]} activities found` : "Activities retrieved",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (name === "get_sleep_data") {
|
|
53
|
+
const nightsMatch = normalized.match(/(\d+) nights?/i);
|
|
54
|
+
return {
|
|
55
|
+
ok: true,
|
|
56
|
+
summary: nightsMatch ? `${nightsMatch[1]} nights retrieved` : "Sleep data retrieved",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (name === "get_heart_rate_trends") {
|
|
60
|
+
const daysMatch = normalized.match(/(\d+) days/i);
|
|
61
|
+
return {
|
|
62
|
+
ok: true,
|
|
63
|
+
summary: daysMatch ? `${daysMatch[1]}-day trend loaded` : "Heart rate trends loaded",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (name === "get_recovery_status") {
|
|
67
|
+
const scoreMatch = normalized.match(/Recovery score:\s*(\d+)/i);
|
|
68
|
+
const recommendationMatch = normalized.match(/Recommendation:\s*(.+)/i);
|
|
69
|
+
if (scoreMatch) {
|
|
70
|
+
const recommendation = recommendationMatch?.[1]?.trim();
|
|
71
|
+
return {
|
|
72
|
+
ok: true,
|
|
73
|
+
summary: recommendation
|
|
74
|
+
? `Score: ${scoreMatch[1]} (${recommendation})`
|
|
75
|
+
: `Score: ${scoreMatch[1]}`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return { ok: true, summary: "Recovery status retrieved" };
|
|
79
|
+
}
|
|
80
|
+
if (name === "get_body_composition") {
|
|
81
|
+
const daysMatch = normalized.match(/(\d+) recorded days/i);
|
|
82
|
+
return {
|
|
83
|
+
ok: true,
|
|
84
|
+
summary: daysMatch ? `${daysMatch[1]} recorded days` : "Body composition retrieved",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (name === "get_stress_levels") {
|
|
88
|
+
const daysMatch = normalized.match(/(\d+) recorded days/i);
|
|
89
|
+
return {
|
|
90
|
+
ok: true,
|
|
91
|
+
summary: daysMatch ? `${daysMatch[1]} stress days loaded` : "Stress data retrieved",
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (name === "get_vo2_max_trends") {
|
|
95
|
+
const daysMatch = normalized.match(/(\d+) recorded days/i);
|
|
96
|
+
return {
|
|
97
|
+
ok: true,
|
|
98
|
+
summary: daysMatch ? `${daysMatch[1]} VO2 entries loaded` : "VO2 max trends loaded",
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (name === "get_training_insights") {
|
|
102
|
+
return {
|
|
103
|
+
ok: true,
|
|
104
|
+
summary: "Training insights summary generated",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const firstLine = normalized.split("\n")[0] ?? normalized;
|
|
108
|
+
return {
|
|
109
|
+
ok: true,
|
|
110
|
+
summary: firstLine.length > 72 ? `${firstLine.slice(0, 69)}...` : firstLine,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
async function runToolCheck(check) {
|
|
114
|
+
try {
|
|
115
|
+
const result = await executeTool(check.name, check.args);
|
|
116
|
+
const { ok, summary } = summarizeToolResult(check.name, result.text);
|
|
117
|
+
return { name: check.name, ok, summary };
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
121
|
+
return {
|
|
122
|
+
name: check.name,
|
|
123
|
+
ok: false,
|
|
124
|
+
summary: message,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export async function runLiveCheck() {
|
|
129
|
+
assertGarminCredentials();
|
|
130
|
+
configureLogger(appConfig.logPath);
|
|
131
|
+
const registered = new Set(listRegisteredToolNames());
|
|
132
|
+
const checks = buildDefaultToolChecks().filter((check) => registered.has(check.name));
|
|
133
|
+
const results = [];
|
|
134
|
+
for (const check of checks) {
|
|
135
|
+
results.push(await runToolCheck(check));
|
|
136
|
+
}
|
|
137
|
+
return results;
|
|
138
|
+
}
|
|
139
|
+
export function printLiveCheckResults(results) {
|
|
140
|
+
console.log("GarminBud live check");
|
|
141
|
+
console.log("");
|
|
142
|
+
for (const result of results) {
|
|
143
|
+
const icon = result.ok ? "✓" : "✗";
|
|
144
|
+
const paddedName = result.name.padEnd(26, " ");
|
|
145
|
+
console.log(` ${paddedName}${icon} ${result.summary}`);
|
|
146
|
+
}
|
|
147
|
+
console.log("");
|
|
148
|
+
const passed = results.filter((result) => result.ok).length;
|
|
149
|
+
const total = results.length;
|
|
150
|
+
if (passed === total) {
|
|
151
|
+
console.log(`All ${total} checks passed. GarminBud is ready to use.`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
console.log(`${passed}/${total} checks passed. Review failures above before connecting an MCP client.`);
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoGpC,wBAAgB,gBAAgB,IAAI,OAAO,CA2G1C"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { authenticateGarmin, clearStoredSession, sessionExists } from "./garmin/auth.js";
|
|
3
|
+
import { closeCache, getCache } from "./garmin/cache.js";
|
|
4
|
+
import { resetGarminClient } from "./garmin/client.js";
|
|
5
|
+
import { createMcpServer } from "./server.js";
|
|
6
|
+
import { createHttpMcpServer, getRemoteConnectorInstructions } from "./httpServer.js";
|
|
7
|
+
import { assertGarminCredentials, appConfig, assertMcpApiKey } from "./config.js";
|
|
8
|
+
import { configureLogger, logger } from "./utils/logger.js";
|
|
9
|
+
import { packageVersion } from "./version.js";
|
|
10
|
+
import { runSetup } from "./setup.js";
|
|
11
|
+
import { printLiveCheckResults, runLiveCheck } from "./check.js";
|
|
12
|
+
// SECTION: CLI Commands
|
|
13
|
+
async function runStart() {
|
|
14
|
+
assertGarminCredentials();
|
|
15
|
+
configureLogger(appConfig.logPath);
|
|
16
|
+
const server = createMcpServer();
|
|
17
|
+
const shutdown = async (signal) => {
|
|
18
|
+
logger.info({ signal }, "Shutting down GarminBud server");
|
|
19
|
+
await server.close();
|
|
20
|
+
closeCache();
|
|
21
|
+
process.exit(0);
|
|
22
|
+
};
|
|
23
|
+
process.once("SIGTERM", () => {
|
|
24
|
+
void shutdown("SIGTERM");
|
|
25
|
+
});
|
|
26
|
+
process.once("SIGINT", () => {
|
|
27
|
+
void shutdown("SIGINT");
|
|
28
|
+
});
|
|
29
|
+
process.once("exit", () => {
|
|
30
|
+
closeCache();
|
|
31
|
+
});
|
|
32
|
+
await server.start();
|
|
33
|
+
}
|
|
34
|
+
async function runAuth() {
|
|
35
|
+
configureLogger(appConfig.logPath);
|
|
36
|
+
clearStoredSession();
|
|
37
|
+
resetGarminClient();
|
|
38
|
+
await authenticateGarmin(true);
|
|
39
|
+
console.log("Garmin authentication successful. Session saved.");
|
|
40
|
+
}
|
|
41
|
+
function runCacheClear() {
|
|
42
|
+
const cache = getCache();
|
|
43
|
+
const removed = cache.clear();
|
|
44
|
+
console.log(`Cleared ${removed} cache entries.`);
|
|
45
|
+
}
|
|
46
|
+
async function runStatus() {
|
|
47
|
+
const cache = getCache();
|
|
48
|
+
const cacheStats = cache.stats();
|
|
49
|
+
console.log("GarminBud status");
|
|
50
|
+
console.log(`Session: ${sessionExists() ? "present" : "missing"}`);
|
|
51
|
+
console.log(`Cache entries: ${cacheStats.entries}`);
|
|
52
|
+
console.log(`Expired cache entries: ${cacheStats.expiredEntries}`);
|
|
53
|
+
}
|
|
54
|
+
async function runCheck() {
|
|
55
|
+
const results = await runLiveCheck();
|
|
56
|
+
printLiveCheckResults(results);
|
|
57
|
+
if (results.some((result) => !result.ok)) {
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function runServe() {
|
|
62
|
+
assertGarminCredentials();
|
|
63
|
+
assertMcpApiKey();
|
|
64
|
+
const server = createHttpMcpServer();
|
|
65
|
+
const shutdown = async (signal) => {
|
|
66
|
+
logger.info({ signal }, "Shutting down GarminBud HTTP server");
|
|
67
|
+
await server.close();
|
|
68
|
+
process.exit(0);
|
|
69
|
+
};
|
|
70
|
+
process.once("SIGTERM", () => {
|
|
71
|
+
void shutdown("SIGTERM");
|
|
72
|
+
});
|
|
73
|
+
process.once("SIGINT", () => {
|
|
74
|
+
void shutdown("SIGINT");
|
|
75
|
+
});
|
|
76
|
+
await server.start();
|
|
77
|
+
console.log(`GarminBud HTTP MCP server running at http://${appConfig.mcpHost}:${appConfig.mcpPort}/mcp`);
|
|
78
|
+
console.log(`Health check: http://${appConfig.mcpHost}:${appConfig.mcpPort}/health`);
|
|
79
|
+
console.log("");
|
|
80
|
+
console.log(getRemoteConnectorInstructions(`https://your-tunnel-url.example.com`));
|
|
81
|
+
}
|
|
82
|
+
export function createCliProgram() {
|
|
83
|
+
const program = new Command();
|
|
84
|
+
program
|
|
85
|
+
.name("garmin-bud")
|
|
86
|
+
.description("GarminBud — MCP server for Garmin Connect fitness data")
|
|
87
|
+
.version(packageVersion);
|
|
88
|
+
program
|
|
89
|
+
.command("start")
|
|
90
|
+
.description("Start the MCP server using stdio transport")
|
|
91
|
+
.action(async () => {
|
|
92
|
+
try {
|
|
93
|
+
await runStart();
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
logger.error({ error }, "Failed to start MCP server");
|
|
97
|
+
process.exitCode = 1;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
program
|
|
101
|
+
.command("auth")
|
|
102
|
+
.description("Force Garmin re-authentication")
|
|
103
|
+
.action(async () => {
|
|
104
|
+
try {
|
|
105
|
+
await runAuth();
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
logger.error({ error }, "Garmin authentication failed");
|
|
109
|
+
console.error(error instanceof Error ? error.message : "Authentication failed");
|
|
110
|
+
process.exitCode = 1;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
const cacheCommand = program.command("cache").description("Manage local cache");
|
|
114
|
+
cacheCommand
|
|
115
|
+
.command("clear")
|
|
116
|
+
.description("Clear all cached Garmin data")
|
|
117
|
+
.action(() => {
|
|
118
|
+
try {
|
|
119
|
+
runCacheClear();
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
logger.error({ error }, "Failed to clear cache");
|
|
123
|
+
process.exitCode = 1;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
program
|
|
127
|
+
.command("serve")
|
|
128
|
+
.description("Start remote MCP server (Streamable HTTP) for web AI connectors")
|
|
129
|
+
.option("-p, --port <number>", "HTTP port", (value) => Number.parseInt(value, 10))
|
|
130
|
+
.option("-H, --host <host>", "Bind host")
|
|
131
|
+
.action(async (options) => {
|
|
132
|
+
try {
|
|
133
|
+
if (options.port) {
|
|
134
|
+
process.env.GARMIN_MCP_PORT = String(options.port);
|
|
135
|
+
}
|
|
136
|
+
if (options.host) {
|
|
137
|
+
process.env.GARMIN_MCP_HOST = options.host;
|
|
138
|
+
}
|
|
139
|
+
await runServe();
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
logger.error({ error }, "Failed to start HTTP MCP server");
|
|
143
|
+
console.error(error instanceof Error ? error.message : "Failed to start HTTP MCP server");
|
|
144
|
+
process.exitCode = 1;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
program
|
|
148
|
+
.command("setup")
|
|
149
|
+
.description("Interactive first-time setup — credentials, auth, and MCP client config")
|
|
150
|
+
.action(async () => {
|
|
151
|
+
try {
|
|
152
|
+
await runSetup();
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
logger.error({ error }, "Setup failed");
|
|
156
|
+
console.error(error instanceof Error ? error.message : "Setup failed");
|
|
157
|
+
process.exitCode = 1;
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
program
|
|
161
|
+
.command("check")
|
|
162
|
+
.description("Run live diagnostics against all GarminBud tools")
|
|
163
|
+
.action(async () => {
|
|
164
|
+
try {
|
|
165
|
+
await runCheck();
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
logger.error({ error }, "Live check failed");
|
|
169
|
+
console.error(error instanceof Error ? error.message : "Live check failed");
|
|
170
|
+
process.exitCode = 1;
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
program
|
|
174
|
+
.command("status")
|
|
175
|
+
.description("Show session and cache status")
|
|
176
|
+
.action(async () => {
|
|
177
|
+
try {
|
|
178
|
+
await runStatus();
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
logger.error({ error }, "Failed to read status");
|
|
182
|
+
process.exitCode = 1;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
return program;
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare function getSessionPath(): string;
|
|
2
|
+
export declare function getProjectRoot(): string;
|
|
3
|
+
export declare function getEnvFilePath(): string;
|
|
4
|
+
export declare function getDistIndexPath(): string;
|
|
5
|
+
export declare function generateMcpApiKey(): string;
|
|
6
|
+
export declare function writeEnvFile(credentials: {
|
|
7
|
+
email: string;
|
|
8
|
+
password: string;
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
}): string;
|
|
11
|
+
export declare const appConfig: {
|
|
12
|
+
readonly garminEmail: string;
|
|
13
|
+
readonly garminPassword: string;
|
|
14
|
+
readonly sessionPath: string;
|
|
15
|
+
logPath: string;
|
|
16
|
+
cachePath: string;
|
|
17
|
+
cacheTtlActivities: number;
|
|
18
|
+
cacheTtlSleep: number;
|
|
19
|
+
cacheTtlStats: number;
|
|
20
|
+
readonly mcpHost: string;
|
|
21
|
+
readonly mcpPort: number;
|
|
22
|
+
readonly mcpApiKey: string;
|
|
23
|
+
readonly anthropicApiKey: string;
|
|
24
|
+
readonly publicUrl: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function assertMcpApiKey(): void;
|
|
27
|
+
export declare function assertGarminCredentials(): void;
|
|
28
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAoBA,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,YAAY,CAAC,WAAW,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CA0BtG;AAED,eAAO,MAAM,SAAS;0BACD,MAAM;6BAGH,MAAM;0BAGT,MAAM;;;;;;sBAQV,MAAM;sBAGN,MAAM;wBAGJ,MAAM;8BAGA,MAAM;wBAGZ,MAAM;CAWxB,CAAC;AAEF,wBAAgB,eAAe,IAAI,IAAI,CAMtC;AAED,wBAAgB,uBAAuB,IAAI,IAAI,CAM9C"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { config as loadEnv } from "dotenv";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
7
|
+
const defaultEnvPath = path.join(projectRoot, ".env");
|
|
8
|
+
loadEnv({ path: defaultEnvPath });
|
|
9
|
+
function readNumber(name, fallback) {
|
|
10
|
+
const value = process.env[name];
|
|
11
|
+
if (!value) {
|
|
12
|
+
return fallback;
|
|
13
|
+
}
|
|
14
|
+
const parsed = Number.parseInt(value, 10);
|
|
15
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
16
|
+
}
|
|
17
|
+
export function getSessionPath() {
|
|
18
|
+
return path.resolve(process.env.GARMIN_SESSION_PATH ?? ".garmin/session.json");
|
|
19
|
+
}
|
|
20
|
+
export function getProjectRoot() {
|
|
21
|
+
return projectRoot;
|
|
22
|
+
}
|
|
23
|
+
export function getEnvFilePath() {
|
|
24
|
+
return defaultEnvPath;
|
|
25
|
+
}
|
|
26
|
+
export function getDistIndexPath() {
|
|
27
|
+
return path.resolve(projectRoot, "dist", "index.js");
|
|
28
|
+
}
|
|
29
|
+
export function generateMcpApiKey() {
|
|
30
|
+
return randomBytes(32).toString("hex");
|
|
31
|
+
}
|
|
32
|
+
export function writeEnvFile(credentials) {
|
|
33
|
+
const envPath = getEnvFilePath();
|
|
34
|
+
const existing = fs.existsSync(envPath) ? fs.readFileSync(envPath, "utf8") : "";
|
|
35
|
+
const existingApiKey = existing.match(/^GARMIN_MCP_API_KEY=(.+)$/m)?.[1]?.trim();
|
|
36
|
+
const apiKey = credentials.apiKey ?? existingApiKey ?? generateMcpApiKey();
|
|
37
|
+
const lines = [
|
|
38
|
+
`GARMIN_EMAIL=${credentials.email}`,
|
|
39
|
+
`GARMIN_PASSWORD=${credentials.password}`,
|
|
40
|
+
`GARMIN_MCP_API_KEY=${apiKey}`,
|
|
41
|
+
"GARMIN_MCP_HOST=127.0.0.1",
|
|
42
|
+
"GARMIN_MCP_PORT=3847",
|
|
43
|
+
"GARMIN_SESSION_PATH=.garmin/session.json",
|
|
44
|
+
"GARMIN_LOG_PATH=.garmin/mcp.log",
|
|
45
|
+
"GARMIN_CACHE_PATH=.garmin/cache.db",
|
|
46
|
+
"CACHE_TTL_ACTIVITIES=1800",
|
|
47
|
+
"CACHE_TTL_SLEEP=7200",
|
|
48
|
+
"CACHE_TTL_STATS=3600",
|
|
49
|
+
"# ANTHROPIC_API_KEY=sk-ant-...",
|
|
50
|
+
"# GARMIN_PUBLIC_URL=https://your-tunnel.trycloudflare.com",
|
|
51
|
+
"",
|
|
52
|
+
];
|
|
53
|
+
fs.writeFileSync(envPath, lines.join("\n"), "utf8");
|
|
54
|
+
loadEnv({ path: envPath, override: true });
|
|
55
|
+
return envPath;
|
|
56
|
+
}
|
|
57
|
+
export const appConfig = {
|
|
58
|
+
get garminEmail() {
|
|
59
|
+
return process.env.GARMIN_EMAIL ?? "";
|
|
60
|
+
},
|
|
61
|
+
get garminPassword() {
|
|
62
|
+
return process.env.GARMIN_PASSWORD ?? "";
|
|
63
|
+
},
|
|
64
|
+
get sessionPath() {
|
|
65
|
+
return getSessionPath();
|
|
66
|
+
},
|
|
67
|
+
logPath: path.resolve(process.env.GARMIN_LOG_PATH ?? ".garmin/mcp.log"),
|
|
68
|
+
cachePath: path.resolve(process.env.GARMIN_CACHE_PATH ?? ".garmin/cache.db"),
|
|
69
|
+
cacheTtlActivities: readNumber("CACHE_TTL_ACTIVITIES", 1800),
|
|
70
|
+
cacheTtlSleep: readNumber("CACHE_TTL_SLEEP", 7200),
|
|
71
|
+
cacheTtlStats: readNumber("CACHE_TTL_STATS", 3600),
|
|
72
|
+
get mcpHost() {
|
|
73
|
+
return process.env.GARMIN_MCP_HOST ?? "127.0.0.1";
|
|
74
|
+
},
|
|
75
|
+
get mcpPort() {
|
|
76
|
+
return readNumber("GARMIN_MCP_PORT", 3847);
|
|
77
|
+
},
|
|
78
|
+
get mcpApiKey() {
|
|
79
|
+
return process.env.GARMIN_MCP_API_KEY ?? "";
|
|
80
|
+
},
|
|
81
|
+
get anthropicApiKey() {
|
|
82
|
+
return process.env.ANTHROPIC_API_KEY ?? "";
|
|
83
|
+
},
|
|
84
|
+
get publicUrl() {
|
|
85
|
+
const envUrl = (process.env.GARMIN_PUBLIC_URL ?? "").replace(/\/$/, "");
|
|
86
|
+
if (envUrl)
|
|
87
|
+
return envUrl;
|
|
88
|
+
try {
|
|
89
|
+
const setupPath = path.join(projectRoot, ".garmin", "watch-setup.json");
|
|
90
|
+
const data = JSON.parse(fs.readFileSync(setupPath, "utf8"));
|
|
91
|
+
return (data.serverUrl ?? "").replace(/\/$/, "");
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return "";
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
export function assertMcpApiKey() {
|
|
99
|
+
if (!appConfig.mcpApiKey) {
|
|
100
|
+
throw new Error('Missing GARMIN_MCP_API_KEY. Run "garmin-bud setup" or add GARMIN_MCP_API_KEY to .env before "garmin-bud serve".');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export function assertGarminCredentials() {
|
|
104
|
+
if (!appConfig.garminEmail || !appConfig.garminPassword) {
|
|
105
|
+
throw new Error("Missing GARMIN_EMAIL or GARMIN_PASSWORD. Copy .env.example to .env and add your credentials.");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AAkBA,wBAAgB,eAAe,IAAI,MAAM,CAkFxC;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMtD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMnD"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { getSetting } from "./appDb.js";
|
|
2
|
+
import { getPendingPairings } from "./pairApi.js";
|
|
3
|
+
import { appConfig } from "./config.js";
|
|
4
|
+
// SECTION: HTML Dashboard
|
|
5
|
+
function escapeHtml(str) {
|
|
6
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
7
|
+
}
|
|
8
|
+
function formatSecondsLeft(expiresAt) {
|
|
9
|
+
const secs = expiresAt - Math.floor(Date.now() / 1000);
|
|
10
|
+
if (secs <= 0)
|
|
11
|
+
return "expired";
|
|
12
|
+
const m = Math.floor(secs / 60);
|
|
13
|
+
const s = secs % 60;
|
|
14
|
+
return m > 0 ? `${m}m ${s}s` : `${s}s`;
|
|
15
|
+
}
|
|
16
|
+
export function renderDashboard() {
|
|
17
|
+
const pendingPairs = getPendingPairings();
|
|
18
|
+
const anthropicKeySet = !!getSetting("anthropic_api_key") || !!appConfig.anthropicApiKey;
|
|
19
|
+
const serverUrl = appConfig.publicUrl || `http://localhost:${appConfig.mcpPort}`;
|
|
20
|
+
const pairingSection = pendingPairs.length === 0
|
|
21
|
+
? `<p class="muted">No pending pairing requests. Open GarminBud on your watch to generate a code.</p>`
|
|
22
|
+
: pendingPairs.map((t) => `
|
|
23
|
+
<div class="pair-card">
|
|
24
|
+
<div class="code">${escapeHtml(t.code)}</div>
|
|
25
|
+
<div class="muted">Expires in ${formatSecondsLeft(t.expires_at)}</div>
|
|
26
|
+
<form method="POST" action="/dashboard/pair/approve">
|
|
27
|
+
<input type="hidden" name="code" value="${escapeHtml(t.code)}">
|
|
28
|
+
<button type="submit" class="btn-approve">Approve</button>
|
|
29
|
+
</form>
|
|
30
|
+
</div>`).join("\n");
|
|
31
|
+
return `<!DOCTYPE html>
|
|
32
|
+
<html lang="en">
|
|
33
|
+
<head>
|
|
34
|
+
<meta charset="UTF-8">
|
|
35
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
36
|
+
<title>GarminBud Dashboard</title>
|
|
37
|
+
<style>
|
|
38
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
39
|
+
body { font-family: system-ui, sans-serif; background: #0f0f0f; color: #e0e0e0; min-height: 100vh; padding: 24px; }
|
|
40
|
+
h1 { font-size: 1.4rem; color: #fff; margin-bottom: 4px; }
|
|
41
|
+
.subtitle { color: #666; font-size: 0.85rem; margin-bottom: 32px; }
|
|
42
|
+
h2 { font-size: 1rem; color: #aaa; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 12px; }
|
|
43
|
+
.section { background: #1a1a1a; border: 1px solid #2a2a2a; border-radius: 10px; padding: 20px; margin-bottom: 24px; max-width: 540px; }
|
|
44
|
+
.pair-card { display: flex; align-items: center; gap: 16px; padding: 12px; background: #111; border-radius: 8px; margin-bottom: 8px; flex-wrap: wrap; }
|
|
45
|
+
.code { font-size: 2rem; font-weight: 700; letter-spacing: 0.3em; color: #fff; font-variant-numeric: tabular-nums; }
|
|
46
|
+
.muted { color: #555; font-size: 0.82rem; }
|
|
47
|
+
.btn-approve { background: #2563eb; color: #fff; border: none; border-radius: 6px; padding: 8px 18px; font-size: 0.9rem; cursor: pointer; }
|
|
48
|
+
.btn-approve:hover { background: #1d4ed8; }
|
|
49
|
+
.status-ok { color: #22c55e; font-size: 0.85rem; }
|
|
50
|
+
.status-missing { color: #f59e0b; font-size: 0.85rem; }
|
|
51
|
+
input[type=text], input[type=password] { background: #111; border: 1px solid #333; color: #e0e0e0; border-radius: 6px; padding: 8px 12px; width: 100%; font-size: 0.9rem; margin-bottom: 10px; }
|
|
52
|
+
input[type=text]:focus, input[type=password]:focus { outline: 2px solid #2563eb; }
|
|
53
|
+
.btn-save { background: #16a34a; color: #fff; border: none; border-radius: 6px; padding: 8px 18px; font-size: 0.9rem; cursor: pointer; }
|
|
54
|
+
.btn-save:hover { background: #15803d; }
|
|
55
|
+
.info { font-size: 0.82rem; color: #555; margin-top: 6px; }
|
|
56
|
+
.alert { background: #1f2937; border: 1px solid #374151; border-radius: 6px; padding: 10px 14px; font-size: 0.85rem; color: #9ca3af; margin-top: 12px; }
|
|
57
|
+
</style>
|
|
58
|
+
</head>
|
|
59
|
+
<body>
|
|
60
|
+
<h1>GarminBud</h1>
|
|
61
|
+
<p class="subtitle">Dashboard · ${escapeHtml(serverUrl)}</p>
|
|
62
|
+
|
|
63
|
+
<div class="section">
|
|
64
|
+
<h2>Watch Pairing</h2>
|
|
65
|
+
${pairingSection}
|
|
66
|
+
<div class="alert">Open GarminBud on your watch → it shows a 6-digit code → approve it here.</div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div class="section">
|
|
70
|
+
<h2>Claude AI</h2>
|
|
71
|
+
<p class="info" style="margin-bottom:12px;">
|
|
72
|
+
Status: ${anthropicKeySet
|
|
73
|
+
? '<span class="status-ok">API key configured</span>'
|
|
74
|
+
: '<span class="status-missing">Not configured</span>'}
|
|
75
|
+
</p>
|
|
76
|
+
<form method="POST" action="/dashboard/settings">
|
|
77
|
+
<label style="font-size:0.85rem;color:#aaa;display:block;margin-bottom:6px;">Anthropic API Key</label>
|
|
78
|
+
<input type="password" name="anthropic_api_key" placeholder="sk-ant-..." autocomplete="off">
|
|
79
|
+
<p class="info">Leave blank to keep existing key. Get yours at console.anthropic.com</p>
|
|
80
|
+
<button type="submit" class="btn-save" style="margin-top:10px;">Save</button>
|
|
81
|
+
</form>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div class="section">
|
|
85
|
+
<h2>Server Info</h2>
|
|
86
|
+
<p style="font-size:0.85rem;">Public URL: <code style="color:#60a5fa">${escapeHtml(serverUrl)}</code></p>
|
|
87
|
+
<p class="info" style="margin-top:6px;">Set GARMIN_PUBLIC_URL in .env to your Cloudflare Tunnel URL for watch pairing.</p>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<script>
|
|
91
|
+
// Auto-refresh every 10s so pairing codes appear without manual reload
|
|
92
|
+
setTimeout(() => location.reload(), 10000);
|
|
93
|
+
</script>
|
|
94
|
+
</body>
|
|
95
|
+
</html>`;
|
|
96
|
+
}
|
|
97
|
+
export function renderPairSuccess(code) {
|
|
98
|
+
return `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Paired</title>
|
|
99
|
+
<style>body{font-family:system-ui;background:#0f0f0f;color:#e0e0e0;display:flex;align-items:center;justify-content:center;min-height:100vh;flex-direction:column;gap:16px;}
|
|
100
|
+
.ok{font-size:3rem;} p{color:#aaa;} a{color:#60a5fa;}</style></head>
|
|
101
|
+
<body><div class="ok">✓</div><p>Watch code <strong>${escapeHtml(code)}</strong> approved. Your watch will connect shortly.</p>
|
|
102
|
+
<a href="/dashboard">← Back to dashboard</a></body></html>`;
|
|
103
|
+
}
|
|
104
|
+
export function renderPairError(msg) {
|
|
105
|
+
return `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Error</title>
|
|
106
|
+
<style>body{font-family:system-ui;background:#0f0f0f;color:#e0e0e0;display:flex;align-items:center;justify-content:center;min-height:100vh;flex-direction:column;gap:16px;}
|
|
107
|
+
.err{font-size:3rem;} p{color:#aaa;} a{color:#60a5fa;}</style></head>
|
|
108
|
+
<body><div class="err">✗</div><p>${escapeHtml(msg)}</p>
|
|
109
|
+
<a href="/dashboard">← Back to dashboard</a></body></html>`;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,0BAA0B;AAE1B,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACxG,CAAC;AAED,SAAS,iBAAiB,CAAC,SAAiB;IAC1C,MAAM,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IACvD,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAChC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;IAChC,MAAM,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,MAAM,YAAY,GAAG,kBAAkB,EAAE,CAAC;IAC1C,MAAM,eAAe,GAAG,CAAC,CAAC,UAAU,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC;IACzF,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,IAAI,oBAAoB,SAAS,CAAC,OAAO,EAAE,CAAC;IAEjF,MAAM,cAAc,GAAG,YAAY,CAAC,MAAM,KAAK,CAAC;QAC9C,CAAC,CAAC,oGAAoG;QACtG,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;;8BAEA,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;0CACN,iBAAiB,CAAC,CAAC,CAAC,UAAU,CAAC;;sDAEnB,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;;;eAGzD,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE1B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCA8B2B,UAAU,CAAC,SAAS,CAAC;;;;MAInD,cAAc;;;;;;;gBAOJ,eAAe;QACvB,CAAC,CAAC,mDAAmD;QACrD,CAAC,CAAC,oDAAoD;;;;;;;;;;;;4EAYc,UAAU,CAAC,SAAS,CAAC;;;;;;;;;QASzF,CAAC;AACT,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO;;;uDAG8C,UAAU,CAAC,IAAI,CAAC;6DACV,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO;;;qCAG4B,UAAU,CAAC,GAAG,CAAC;6DACS,CAAC;AAC9D,CAAC"}
|