@daobrew/wellness-mcp 0.1.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/README.md +98 -0
- package/SKILL.md +190 -0
- package/audio/earth_breathing.m4a +0 -0
- package/audio/earth_meditation.m4a +0 -0
- package/audio/earth_zhanZhuang.m4a +0 -0
- package/audio/fire_breathing.m4a +0 -0
- package/audio/fire_meditation.m4a +0 -0
- package/audio/fire_zhanZhuang.m4a +0 -0
- package/audio/metal_breathing.m4a +0 -0
- package/audio/metal_meditation.m4a +0 -0
- package/audio/metal_zhanZhuang.m4a +0 -0
- package/audio/water_breathing.m4a +0 -0
- package/audio/water_meditation.m4a +0 -0
- package/audio/water_zhanZhuang.m4a +0 -0
- package/audio/wood_breathing.m4a +0 -0
- package/audio/wood_meditation.m4a +0 -0
- package/audio/wood_zhanZhuang.m4a +0 -0
- package/dist/src/audio.d.ts +13 -0
- package/dist/src/audio.js +88 -0
- package/dist/src/cache.d.ts +7 -0
- package/dist/src/cache.js +31 -0
- package/dist/src/client.d.ts +22 -0
- package/dist/src/client.js +65 -0
- package/dist/src/cooldown.d.ts +5 -0
- package/dist/src/cooldown.js +35 -0
- package/dist/src/headphones.d.ts +6 -0
- package/dist/src/headphones.js +50 -0
- package/dist/src/health/google-fit.d.ts +13 -0
- package/dist/src/health/google-fit.js +108 -0
- package/dist/src/health/index.d.ts +6 -0
- package/dist/src/health/index.js +42 -0
- package/dist/src/health/oauth.d.ts +6 -0
- package/dist/src/health/oauth.js +69 -0
- package/dist/src/health/oura.d.ts +14 -0
- package/dist/src/health/oura.js +130 -0
- package/dist/src/health/sync.d.ts +7 -0
- package/dist/src/health/sync.js +194 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +107 -0
- package/dist/src/mock.d.ts +8 -0
- package/dist/src/mock.js +176 -0
- package/dist/src/preferences.d.ts +13 -0
- package/dist/src/preferences.js +47 -0
- package/dist/src/session.d.ts +15 -0
- package/dist/src/session.js +40 -0
- package/dist/src/setup-cli.js +2 -0
- package/dist/src/setup.d.ts +17 -0
- package/dist/src/setup.js +323 -0
- package/dist/src/tools.d.ts +4 -0
- package/dist/src/tools.js +420 -0
- package/dist/src/types.d.ts +86 -0
- package/dist/src/types.js +52 -0
- package/dist/tests/audio.test.d.ts +1 -0
- package/dist/tests/audio.test.js +67 -0
- package/dist/tests/cache.test.d.ts +1 -0
- package/dist/tests/cache.test.js +61 -0
- package/dist/tests/client.test.d.ts +1 -0
- package/dist/tests/client.test.js +95 -0
- package/dist/tests/cooldown.test.d.ts +1 -0
- package/dist/tests/cooldown.test.js +66 -0
- package/dist/tests/e2e.test.d.ts +1 -0
- package/dist/tests/e2e.test.js +144 -0
- package/dist/tests/guards.test.d.ts +1 -0
- package/dist/tests/guards.test.js +169 -0
- package/dist/tests/headphones.test.d.ts +1 -0
- package/dist/tests/headphones.test.js +46 -0
- package/dist/tests/mock.test.d.ts +1 -0
- package/dist/tests/mock.test.js +194 -0
- package/dist/tests/preferences.test.d.ts +1 -0
- package/dist/tests/preferences.test.js +71 -0
- package/dist/tests/session.test.d.ts +1 -0
- package/dist/tests/session.test.js +85 -0
- package/dist/tests/sync.test.d.ts +1 -0
- package/dist/tests/sync.test.js +54 -0
- package/package.json +29 -0
- package/src/setup-cli.js +2 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const node_test_1 = require("node:test");
|
|
37
|
+
const assert = __importStar(require("node:assert/strict"));
|
|
38
|
+
const mock_js_1 = require("../src/mock.js");
|
|
39
|
+
const types_js_1 = require("../src/types.js");
|
|
40
|
+
const ELEMENTS = ["wood", "fire", "earth", "metal", "water"];
|
|
41
|
+
const QUADRANTS = ["peak", "pushing_it", "recharging", "burnout"];
|
|
42
|
+
(0, node_test_1.describe)("daobrew_get_wellness_state", () => {
|
|
43
|
+
(0, node_test_1.it)("returns all required fields with correct types", () => {
|
|
44
|
+
const state = (0, mock_js_1.mockWellnessState)();
|
|
45
|
+
assert.ok(typeof state.yin === "number" && state.yin >= 0 && state.yin <= 100);
|
|
46
|
+
assert.ok(typeof state.yang === "number" && state.yang >= 0 && state.yang <= 100);
|
|
47
|
+
assert.ok(QUADRANTS.includes(state.quadrant));
|
|
48
|
+
assert.ok(typeof state.quadrant_label === "string" && state.quadrant_label.length > 0);
|
|
49
|
+
assert.ok(typeof state.quadrant_description === "string" && state.quadrant_description.length > 0);
|
|
50
|
+
assert.ok(typeof state.yin_label === "string" && state.yin_label.length > 0);
|
|
51
|
+
assert.ok(typeof state.yang_label === "string" && state.yang_label.length > 0);
|
|
52
|
+
assert.ok(Array.isArray(state.active_elements));
|
|
53
|
+
ELEMENTS.forEach(e => assert.ok(typeof state.element_scores[e] === "number"));
|
|
54
|
+
assert.ok(typeof state.top_signal === "string");
|
|
55
|
+
assert.ok(typeof state.recommendation === "string");
|
|
56
|
+
assert.strictEqual(state.data_tier, "tier_2");
|
|
57
|
+
assert.ok(!isNaN(Date.parse(state.last_updated))); // valid ISO 8601
|
|
58
|
+
});
|
|
59
|
+
(0, node_test_1.it)("quadrant matches yin/yang thresholds", () => {
|
|
60
|
+
for (let i = 0; i < 20; i++) {
|
|
61
|
+
const state = (0, mock_js_1.mockWellnessState)();
|
|
62
|
+
if (state.yin >= 50 && state.yang >= 50)
|
|
63
|
+
assert.strictEqual(state.quadrant, "peak");
|
|
64
|
+
if (state.yin < 50 && state.yang >= 50)
|
|
65
|
+
assert.strictEqual(state.quadrant, "pushing_it");
|
|
66
|
+
if (state.yin >= 50 && state.yang < 50)
|
|
67
|
+
assert.strictEqual(state.quadrant, "recharging");
|
|
68
|
+
if (state.yin < 50 && state.yang < 50)
|
|
69
|
+
assert.strictEqual(state.quadrant, "burnout");
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
(0, node_test_1.it)("active_elements matches elements with score >= 50", () => {
|
|
73
|
+
for (let i = 0; i < 10; i++) {
|
|
74
|
+
const state = (0, mock_js_1.mockWellnessState)();
|
|
75
|
+
const expected = ELEMENTS.filter(e => state.element_scores[e] >= 50);
|
|
76
|
+
assert.deepStrictEqual(state.active_elements.sort(), expected.sort());
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
(0, node_test_1.it)("cycles through different quadrants across calls", () => {
|
|
80
|
+
const quadrants = new Set();
|
|
81
|
+
for (let i = 0; i < 12; i++)
|
|
82
|
+
quadrants.add((0, mock_js_1.mockWellnessState)().quadrant);
|
|
83
|
+
assert.ok(quadrants.size >= 2, `Expected >= 2 quadrants, got ${quadrants.size}`);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
(0, node_test_1.describe)("daobrew_get_element_detail", () => {
|
|
87
|
+
ELEMENTS.forEach(element => {
|
|
88
|
+
(0, node_test_1.it)(`returns valid detail for ${element}`, () => {
|
|
89
|
+
const detail = (0, mock_js_1.mockElementDetail)(element);
|
|
90
|
+
assert.strictEqual(detail.element, element);
|
|
91
|
+
assert.strictEqual(detail.label, `${types_js_1.ELEMENT_LABELS[element]} · ${types_js_1.ELEMENT_ORGANS[element]}`);
|
|
92
|
+
assert.strictEqual(detail.intervention.genre, types_js_1.ELEMENT_GENRES[element]);
|
|
93
|
+
assert.strictEqual(detail.intervention.type, "breathing");
|
|
94
|
+
assert.strictEqual(detail.intervention.breathing_rate_bpm, 6.0);
|
|
95
|
+
assert.ok(detail.evidence.length > 0);
|
|
96
|
+
assert.ok(typeof detail.headline === "string");
|
|
97
|
+
assert.ok(typeof detail.why_this_practice === "string");
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
(0, node_test_1.describe)("daobrew_start_breathing_session", () => {
|
|
102
|
+
(0, node_test_1.it)("text tier returns valid session", () => {
|
|
103
|
+
const session = (0, mock_js_1.mockStartSession)("wood", "text");
|
|
104
|
+
assert.strictEqual(session.status, "started");
|
|
105
|
+
assert.ok(session.session_id.startsWith("sess_"));
|
|
106
|
+
assert.strictEqual(session.element, "wood");
|
|
107
|
+
assert.strictEqual(session.genre, "ambient_downtempo");
|
|
108
|
+
});
|
|
109
|
+
(0, node_test_1.it)("audio tier returns session without script", () => {
|
|
110
|
+
const session = (0, mock_js_1.mockStartSession)("fire", "audio");
|
|
111
|
+
assert.strictEqual(session.status, "started");
|
|
112
|
+
assert.strictEqual(session.tier, "audio");
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
(0, node_test_1.describe)("daobrew_get_session_result", () => {
|
|
116
|
+
(0, node_test_1.it)("returns improvement metrics", () => {
|
|
117
|
+
const session = (0, mock_js_1.mockStartSession)("earth", "text");
|
|
118
|
+
const result = (0, mock_js_1.mockSessionResult)(session.session_id);
|
|
119
|
+
assert.strictEqual(result.completed, true);
|
|
120
|
+
assert.ok(result.hrv_before < result.hrv_after);
|
|
121
|
+
assert.ok(result.hrv_change_pct >= 20 && result.hrv_change_pct <= 60);
|
|
122
|
+
assert.ok(result.hr_before > result.hr_after);
|
|
123
|
+
assert.ok(typeof result.summary === "string" && result.summary.length > 0);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
(0, node_test_1.describe)("daobrew_get_session_history", () => {
|
|
127
|
+
(0, node_test_1.it)("returns sorted entries within date range", () => {
|
|
128
|
+
const entries = (0, mock_js_1.mockSessionHistory)(7);
|
|
129
|
+
assert.ok(entries.length >= 3 && entries.length <= 14);
|
|
130
|
+
for (let i = 1; i < entries.length; i++) {
|
|
131
|
+
assert.ok(new Date(entries[i - 1].timestamp) >= new Date(entries[i].timestamp), "not sorted desc");
|
|
132
|
+
}
|
|
133
|
+
entries.forEach(e => {
|
|
134
|
+
assert.ok(ELEMENTS.includes(e.element));
|
|
135
|
+
assert.ok(e.session_id.length > 0);
|
|
136
|
+
assert.ok(typeof e.hrv_change_pct === "number");
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
(0, node_test_1.describe)("mock first call always stressed", () => {
|
|
141
|
+
(0, node_test_1.it)("first mockWellnessState() call has active_elements.length > 0", () => {
|
|
142
|
+
(0, mock_js_1._resetWellnessCallCount)();
|
|
143
|
+
const state = (0, mock_js_1.mockWellnessState)();
|
|
144
|
+
assert.ok(state.active_elements.length > 0, "First call should always be stressed");
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
(0, node_test_1.describe)("mock demo mode", () => {
|
|
148
|
+
(0, node_test_1.it)("isDemo=true always produces stressed output", () => {
|
|
149
|
+
(0, mock_js_1._resetWellnessCallCount)();
|
|
150
|
+
for (let i = 0; i < 5; i++) {
|
|
151
|
+
const state = (0, mock_js_1.mockWellnessState)(true);
|
|
152
|
+
assert.ok(state.active_elements.length > 0, `Demo call ${i + 1} should be stressed`);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
(0, node_test_1.describe)("mock session result labeled", () => {
|
|
157
|
+
(0, node_test_1.it)("summary starts with [Mock]", () => {
|
|
158
|
+
const session = (0, mock_js_1.mockStartSession)("earth", "text");
|
|
159
|
+
const result = (0, mock_js_1.mockSessionResult)(session.session_id);
|
|
160
|
+
assert.ok(result.summary.startsWith("[Mock]"), `Expected [Mock] prefix, got: ${result.summary.substring(0, 20)}`);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
(0, node_test_1.describe)("daobrew_stop_session", () => {
|
|
164
|
+
(0, node_test_1.it)("returns no_active_session when nothing playing", async () => {
|
|
165
|
+
const { handleToolCall } = await import("../src/tools.js");
|
|
166
|
+
const result = await handleToolCall("daobrew_stop_session", {}, true);
|
|
167
|
+
assert.strictEqual(result.status, "no_active_session");
|
|
168
|
+
assert.strictEqual(result.duration_played, null);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
(0, node_test_1.describe)("get_wellness_state caching", () => {
|
|
172
|
+
(0, node_test_1.it)("returns cache_age_seconds = 0 on first call", async () => {
|
|
173
|
+
const { handleToolCall } = await import("../src/tools.js");
|
|
174
|
+
const result = await handleToolCall("daobrew_get_wellness_state", { force_refresh: true }, true);
|
|
175
|
+
assert.strictEqual(result.cache_age_seconds, 0);
|
|
176
|
+
});
|
|
177
|
+
(0, node_test_1.it)("returns cached data on second call without force_refresh", async () => {
|
|
178
|
+
const { handleToolCall } = await import("../src/tools.js");
|
|
179
|
+
const r1 = await handleToolCall("daobrew_get_wellness_state", { force_refresh: true }, true);
|
|
180
|
+
const r2 = await handleToolCall("daobrew_get_wellness_state", {}, true);
|
|
181
|
+
assert.ok(r2.cache_age_seconds >= 0);
|
|
182
|
+
assert.strictEqual(r2.yin, r1.yin);
|
|
183
|
+
assert.strictEqual(r2.yang, r1.yang);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
(0, node_test_1.describe)("start_breathing_session guards", () => {
|
|
187
|
+
(0, node_test_1.it)("rejects ambient mode without optin", async () => {
|
|
188
|
+
const { save } = await import("../src/preferences.js");
|
|
189
|
+
save({ disabled: false, ambient_optin: false });
|
|
190
|
+
const { handleToolCall } = await import("../src/tools.js");
|
|
191
|
+
const result = await handleToolCall("daobrew_start_breathing_session", { element: "wood", mode: "ambient" }, true);
|
|
192
|
+
assert.strictEqual(result.status, "requires_optin");
|
|
193
|
+
});
|
|
194
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const node_test_1 = require("node:test");
|
|
37
|
+
const assert = __importStar(require("node:assert/strict"));
|
|
38
|
+
const fs_1 = require("fs");
|
|
39
|
+
const path_1 = require("path");
|
|
40
|
+
const os_1 = require("os");
|
|
41
|
+
const preferences_js_1 = require("../src/preferences.js");
|
|
42
|
+
const PREFS_FILE = (0, path_1.join)((0, os_1.homedir)(), ".daobrew", "prefs.json");
|
|
43
|
+
(0, node_test_1.describe)("preferences", () => {
|
|
44
|
+
(0, node_test_1.beforeEach)(() => {
|
|
45
|
+
if ((0, fs_1.existsSync)(PREFS_FILE))
|
|
46
|
+
(0, fs_1.unlinkSync)(PREFS_FILE);
|
|
47
|
+
});
|
|
48
|
+
(0, node_test_1.it)("load() returns defaults when no file exists", () => {
|
|
49
|
+
const prefs = (0, preferences_js_1.load)();
|
|
50
|
+
assert.strictEqual(prefs.ambient_optin, false);
|
|
51
|
+
assert.strictEqual(prefs.preferred_volume, 0.3);
|
|
52
|
+
assert.strictEqual(prefs.session_count, 0);
|
|
53
|
+
assert.strictEqual(prefs.voiceover, true);
|
|
54
|
+
});
|
|
55
|
+
(0, node_test_1.it)("save() persists and load() reads back", () => {
|
|
56
|
+
(0, preferences_js_1.save)({ ambient_optin: true, preferred_volume: 0.5 });
|
|
57
|
+
const prefs = (0, preferences_js_1.load)();
|
|
58
|
+
assert.strictEqual(prefs.ambient_optin, true);
|
|
59
|
+
assert.strictEqual(prefs.preferred_volume, 0.5);
|
|
60
|
+
assert.strictEqual(prefs.disabled, false);
|
|
61
|
+
});
|
|
62
|
+
(0, node_test_1.it)("incrementSessionCount auto-disables voiceover at 3", () => {
|
|
63
|
+
(0, preferences_js_1.save)({ session_count: 0, voiceover: true });
|
|
64
|
+
(0, preferences_js_1.incrementSessionCount)(); // 1
|
|
65
|
+
assert.strictEqual((0, preferences_js_1.load)().voiceover, true);
|
|
66
|
+
(0, preferences_js_1.incrementSessionCount)(); // 2
|
|
67
|
+
assert.strictEqual((0, preferences_js_1.load)().voiceover, true);
|
|
68
|
+
(0, preferences_js_1.incrementSessionCount)(); // 3
|
|
69
|
+
assert.strictEqual((0, preferences_js_1.load)().voiceover, false);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const node_test_1 = require("node:test");
|
|
37
|
+
const assert = __importStar(require("node:assert/strict"));
|
|
38
|
+
const session_js_1 = require("../src/session.js");
|
|
39
|
+
(0, node_test_1.describe)("session state", () => {
|
|
40
|
+
(0, node_test_1.beforeEach)(() => {
|
|
41
|
+
(0, session_js_1.clearSession)();
|
|
42
|
+
});
|
|
43
|
+
(0, node_test_1.it)("starts with no active session", () => {
|
|
44
|
+
assert.strictEqual((0, session_js_1.getActiveSession)(), null);
|
|
45
|
+
assert.strictEqual((0, session_js_1.isSessionRunning)(), false);
|
|
46
|
+
assert.strictEqual((0, session_js_1.durationPlayed)(), null);
|
|
47
|
+
});
|
|
48
|
+
(0, node_test_1.it)("startSession/getActiveSession round-trips", () => {
|
|
49
|
+
const session = {
|
|
50
|
+
sessionId: "sess_test", pid: 0, element: "wood",
|
|
51
|
+
genre: "ambient_downtempo", duration: 300, startTime: Date.now(), mode: "ondemand",
|
|
52
|
+
};
|
|
53
|
+
(0, session_js_1.startSession)(session);
|
|
54
|
+
assert.strictEqual((0, session_js_1.isSessionRunning)(), true);
|
|
55
|
+
assert.strictEqual((0, session_js_1.getActiveSession)()?.sessionId, "sess_test");
|
|
56
|
+
assert.strictEqual((0, session_js_1.getActiveSession)()?.mode, "ondemand");
|
|
57
|
+
});
|
|
58
|
+
(0, node_test_1.it)("clearSession returns previous and clears state", () => {
|
|
59
|
+
const session = {
|
|
60
|
+
sessionId: "sess_clear", pid: 99, element: "fire",
|
|
61
|
+
genre: "lofi_chill_jazz", duration: 300, startTime: Date.now(), mode: "ambient",
|
|
62
|
+
};
|
|
63
|
+
(0, session_js_1.startSession)(session);
|
|
64
|
+
const prev = (0, session_js_1.clearSession)();
|
|
65
|
+
assert.strictEqual(prev?.sessionId, "sess_clear");
|
|
66
|
+
assert.strictEqual((0, session_js_1.isSessionRunning)(), false);
|
|
67
|
+
});
|
|
68
|
+
(0, node_test_1.it)("durationPlayed returns elapsed seconds", () => {
|
|
69
|
+
const session = {
|
|
70
|
+
sessionId: "sess_dur", pid: 0, element: "earth",
|
|
71
|
+
genre: "acoustic_folk_calm", duration: 300, startTime: Date.now() - 5000, mode: "ondemand",
|
|
72
|
+
};
|
|
73
|
+
(0, session_js_1.startSession)(session);
|
|
74
|
+
const played = (0, session_js_1.durationPlayed)();
|
|
75
|
+
assert.ok(played !== null && played >= 4 && played <= 6);
|
|
76
|
+
});
|
|
77
|
+
(0, node_test_1.it)("declines second session when one is already running", () => {
|
|
78
|
+
const s1 = {
|
|
79
|
+
sessionId: "sess_1", pid: 0, element: "wood",
|
|
80
|
+
genre: "ambient_downtempo", duration: 300, startTime: Date.now(), mode: "ondemand",
|
|
81
|
+
};
|
|
82
|
+
(0, session_js_1.startSession)(s1);
|
|
83
|
+
assert.strictEqual((0, session_js_1.isSessionRunning)(), true);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const node_test_1 = require("node:test");
|
|
37
|
+
const assert = __importStar(require("node:assert/strict"));
|
|
38
|
+
const sync_js_1 = require("../src/health/sync.js");
|
|
39
|
+
const client_js_1 = require("../src/client.js");
|
|
40
|
+
(0, node_test_1.describe)("syncAllConnectedSources", () => {
|
|
41
|
+
(0, node_test_1.it)("returns empty array when no sources connected", async () => {
|
|
42
|
+
// Create a client pointing at nothing (won't be called since no tokens exist)
|
|
43
|
+
const client = new client_js_1.DaoBrewClient({ apiKey: "dbk_test", baseUrl: "http://localhost:1", timeoutMs: 1000 });
|
|
44
|
+
const results = await (0, sync_js_1.syncAllConnectedSources)(client);
|
|
45
|
+
// No tokens exist at ~/.daobrew/oura-token.json or google-fit-token.json
|
|
46
|
+
// so both isConnected() checks return false and no sync happens
|
|
47
|
+
assert.ok(Array.isArray(results));
|
|
48
|
+
// Should have 0 results since nothing is connected
|
|
49
|
+
for (const r of results) {
|
|
50
|
+
assert.ok(typeof r.source === "string");
|
|
51
|
+
assert.ok(typeof r.samples_pushed === "number");
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@daobrew/wellness-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Wellness MCP server — biometric stress detection & recovery for AI agents",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"daobrew-wellness-mcp": "dist/src/index.js",
|
|
8
|
+
"daobrew-wellness-setup": "dist/src/setup-cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": ["dist/", "audio/", "SKILL.md", "README.md", "src/setup-cli.js"],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"setup": "node dist/src/setup-cli.js",
|
|
14
|
+
"dev": "ts-node src/index.ts",
|
|
15
|
+
"mock": "ts-node src/index.ts --mock",
|
|
16
|
+
"postinstall": "echo '\\n Run: npx daobrew-wellness-setup\\n to complete DaoBrew configuration.\\n'",
|
|
17
|
+
"test": "node --test --test-concurrency=1 dist/tests/mock.test.js dist/tests/preferences.test.js dist/tests/headphones.test.js dist/tests/session.test.js dist/tests/cooldown.test.js dist/tests/cache.test.js dist/tests/e2e.test.js dist/tests/audio.test.js dist/tests/guards.test.js dist/tests/client.test.js dist/tests/sync.test.js"
|
|
18
|
+
},
|
|
19
|
+
"keywords": ["mcp", "wellness", "health", "biometrics", "tcm", "stress", "breathing"],
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^25.5.2",
|
|
26
|
+
"ts-node": "^10.9.2",
|
|
27
|
+
"typescript": "^6.0.2"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/setup-cli.js
ADDED