@trainheroic-unofficial/core 0.3.0 → 0.4.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/dist/index.d.mts +17 -1
- package/dist/index.mjs +219 -59
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -63,6 +63,22 @@ declare function confirmGate(server: McpServer, requestId: string | number | und
|
|
|
63
63
|
/** Read-only coach/athlete queries. Exercise lookups live in the exercise store tools. */
|
|
64
64
|
declare function registerReadTools(server: McpServer, ctx: ToolContext): void;
|
|
65
65
|
//#endregion
|
|
66
|
+
//#region src/tools/athlete-training.d.ts
|
|
67
|
+
/**
|
|
68
|
+
* Athlete tools need only the client (no exercise-library index), so they take a narrower
|
|
69
|
+
* context than the coach tools. A full `ToolContext` also satisfies this, so the same ctx
|
|
70
|
+
* object can be passed.
|
|
71
|
+
*/
|
|
72
|
+
type AthleteContext = {
|
|
73
|
+
client: TrainHeroicClient;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Live tools over the logged-in user's own training (history, scheduled/completed workouts,
|
|
77
|
+
* PRs, working maxes), plus a gated set-logging write. The athlete user id is
|
|
78
|
+
* resolved once from /user/simple and reused across tools.
|
|
79
|
+
*/
|
|
80
|
+
declare function registerAthleteTrainingTools(server: McpServer, ctx: AthleteContext): void;
|
|
81
|
+
//#endregion
|
|
66
82
|
//#region src/tools/athletes.d.ts
|
|
67
83
|
/**
|
|
68
84
|
* Athlete management. TrainHeroic has no "create athlete" primitive: a coach invites a
|
|
@@ -100,4 +116,4 @@ declare function registerWorkoutTools(server: McpServer, ctx: ToolContext): void
|
|
|
100
116
|
/** Live messaging: list/read conversations, draft a message, and the gated send/delete. */
|
|
101
117
|
declare function registerMessagingTools(server: McpServer, ctx: ToolContext): void;
|
|
102
118
|
//#endregion
|
|
103
|
-
export { BudgetHint, DEFAULT_RESULT_BUDGET, DESTRUCTIVE, NOT_CONFIRMED, READ, SYNC, ToolContext, apiCall, attempt, boundedSerialize, confirmGate, errorResult, idParam, jsonResult, registerAnalyticsTools, registerAthleteTools, registerExerciseTools, registerMessagingTools, registerReadTools, registerTeamTools, registerWorkoutTools, resultBudget, toId };
|
|
119
|
+
export { AthleteContext, BudgetHint, DEFAULT_RESULT_BUDGET, DESTRUCTIVE, NOT_CONFIRMED, READ, SYNC, ToolContext, apiCall, attempt, boundedSerialize, confirmGate, errorResult, idParam, jsonResult, registerAnalyticsTools, registerAthleteTools, registerAthleteTrainingTools, registerExerciseTools, registerMessagingTools, registerReadTools, registerTeamTools, registerWorkoutTools, resultBudget, toId };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { blockSpecSchema, commentDraftSchema, exerciseCreateSchema, idArgSchema, parseWorkoutDate } from "@trainheroic-unofficial/dto";
|
|
1
|
+
import { blockSpecSchema, commentDraftSchema, dateString, exerciseCreateSchema, idArgSchema, logSetArgsSchema, parseWorkoutDate } from "@trainheroic-unofficial/dto";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { buildCommentPayload, buildSession, collectAdvisories, deleteComment, fetchStreams, publishSession, readLive, readSession, removeSession, sendComment } from "@trainheroic-unofficial/js";
|
|
3
|
+
import { buildCommentPayload, buildSession, coerceInt, collectAdvisories, deleteComment, exerciseUnits, fetchAthletePrefs, fetchAthleteProfileSummary, fetchAthleteUser, fetchAthleteWorkouts, fetchExerciseHistoryDetail, fetchExerciseHistoryList, fetchExerciseStats, fetchLeaderboard, fetchPersonalRecords, fetchStreams, fetchWorkingMaxes, isRecord, logAthleteSet, presentAthleteWorkouts, presentExerciseHistory, publishSession, readLive, readSession, removeSession, searchExerciseHistory, sendComment } from "@trainheroic-unofficial/js";
|
|
4
4
|
//#region src/context.ts
|
|
5
5
|
/** A tool argument that accepts a numeric id as a number or a string of digits. */
|
|
6
6
|
const idParam = idArgSchema;
|
|
@@ -300,42 +300,175 @@ function registerEntityReads(server, ctx) {
|
|
|
300
300
|
inputSchema: { programId: idParam },
|
|
301
301
|
annotations: READ
|
|
302
302
|
}, ({ programId }) => apiCall(ctx, "GET", `/3.0/coach/program/${enc(programId)}`, void 0, "This is a large, deep object. If it is truncated, fetch a narrower view (a specific session) instead."));
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
303
|
+
}
|
|
304
|
+
/** Read-only coach/athlete queries. Exercise lookups live in the exercise store tools. */
|
|
305
|
+
function registerReadTools(server, ctx) {
|
|
306
|
+
registerRosterReads(server, ctx);
|
|
307
|
+
registerEntityReads(server, ctx);
|
|
308
|
+
}
|
|
309
|
+
//#endregion
|
|
310
|
+
//#region src/tools/athlete-training.ts
|
|
311
|
+
/** Identity, profile, prefs, working maxes, leaderboard. */
|
|
312
|
+
function registerProfileTools(server, ctx, whoami, userId) {
|
|
313
|
+
server.registerTool("athlete_whoami", {
|
|
314
|
+
title: "Who am I (athlete)",
|
|
315
|
+
description: "The logged-in account's identity (id, name, roles) from /user/simple.",
|
|
316
|
+
inputSchema: {},
|
|
317
|
+
annotations: READ
|
|
318
|
+
}, () => attempt(async () => jsonResult(await whoami())));
|
|
319
|
+
server.registerTool("athlete_profile", {
|
|
320
|
+
title: "Athlete profile + lifetime totals",
|
|
321
|
+
description: "Lifetime training totals (reps, volume, sessions, first/last logged) plus the profile (name, units, dob). Set useMetric for kg/metric totals.",
|
|
322
|
+
inputSchema: { useMetric: z.boolean().optional() },
|
|
323
|
+
annotations: READ
|
|
324
|
+
}, ({ useMetric }) => attempt(async () => {
|
|
325
|
+
const id = await userId();
|
|
326
|
+
const [summary, user] = await Promise.all([fetchAthleteProfileSummary(ctx.client, id, useMetric ?? false), fetchAthleteUser(ctx.client, id)]);
|
|
327
|
+
return jsonResult({
|
|
328
|
+
summary,
|
|
329
|
+
user
|
|
330
|
+
});
|
|
331
|
+
}));
|
|
332
|
+
server.registerTool("athlete_prefs", {
|
|
333
|
+
title: "Athlete preferences",
|
|
334
|
+
description: "Notification and display preference flags for the athlete account.",
|
|
335
|
+
inputSchema: {},
|
|
336
|
+
annotations: READ
|
|
337
|
+
}, () => attempt(async () => jsonResult(await fetchAthletePrefs(ctx.client))));
|
|
338
|
+
server.registerTool("athlete_working_maxes", {
|
|
339
|
+
title: "Working maxes",
|
|
340
|
+
description: "The athlete's working max per exercise (drives % prescriptions).",
|
|
341
|
+
inputSchema: {},
|
|
342
|
+
annotations: READ
|
|
343
|
+
}, () => attempt(async () => jsonResult(await fetchWorkingMaxes(ctx.client))));
|
|
344
|
+
server.registerTool("athlete_leaderboard", {
|
|
345
|
+
title: "Benchmark leaderboard",
|
|
346
|
+
description: "Leaderboard for a benchmark/test workout by its workout id.",
|
|
306
347
|
inputSchema: {
|
|
348
|
+
workoutId: idParam,
|
|
307
349
|
page: z.number().int().positive().optional(),
|
|
308
|
-
pageSize: z.number().int().positive().optional()
|
|
350
|
+
pageSize: z.number().int().positive().max(200).optional(),
|
|
351
|
+
gender: z.number().int().optional()
|
|
309
352
|
},
|
|
310
353
|
annotations: READ
|
|
311
|
-
}, ({ page, pageSize }) => {
|
|
312
|
-
const
|
|
313
|
-
if (page !== void 0)
|
|
314
|
-
if (pageSize !== void 0)
|
|
315
|
-
|
|
316
|
-
return
|
|
317
|
-
});
|
|
354
|
+
}, ({ workoutId, page, pageSize, gender }) => attempt(async () => {
|
|
355
|
+
const opts = {};
|
|
356
|
+
if (page !== void 0) opts.page = page;
|
|
357
|
+
if (pageSize !== void 0) opts.pageSize = pageSize;
|
|
358
|
+
if (gender !== void 0) opts.gender = gender;
|
|
359
|
+
return jsonResult(await fetchLeaderboard(ctx.client, toId(workoutId), opts));
|
|
360
|
+
}));
|
|
361
|
+
}
|
|
362
|
+
/** Workouts, exercise catalog, per-exercise history/PRs/stats. */
|
|
363
|
+
function registerExerciseTools$1(server, ctx, userId) {
|
|
318
364
|
server.registerTool("athlete_workouts", {
|
|
319
|
-
title: "
|
|
320
|
-
description: "
|
|
365
|
+
title: "Workouts in a date range",
|
|
366
|
+
description: "Scheduled + completed workouts in an inclusive YYYY-MM-DD window, flattened to blocks/exercises with per-set prescriptions and positional units. Set raw:true for the untouched API objects. Narrow the window if the result is truncated.",
|
|
321
367
|
inputSchema: {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
368
|
+
startDate: dateString,
|
|
369
|
+
endDate: dateString,
|
|
370
|
+
raw: z.boolean().optional()
|
|
325
371
|
},
|
|
326
372
|
annotations: READ
|
|
327
|
-
}, ({
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
373
|
+
}, ({ startDate, endDate, raw }) => attempt(async () => {
|
|
374
|
+
const workouts = await fetchAthleteWorkouts(ctx.client, startDate, endDate);
|
|
375
|
+
return jsonResult(raw === true ? workouts : presentAthleteWorkouts(workouts), { hint: "Narrow startDate/endDate to shrink this result." });
|
|
376
|
+
}));
|
|
377
|
+
server.registerTool("athlete_exercises", {
|
|
378
|
+
title: "Search logged exercises",
|
|
379
|
+
description: "The exercises the athlete has logged (id + title + positional units). Pass q to free-text search by name; use the returned id with athlete_exercise_history / _stats.",
|
|
380
|
+
inputSchema: {
|
|
381
|
+
q: z.string().optional(),
|
|
382
|
+
limit: z.number().int().positive().max(200).optional()
|
|
383
|
+
},
|
|
384
|
+
annotations: READ
|
|
385
|
+
}, ({ q, limit }) => attempt(async () => {
|
|
386
|
+
return jsonResult((q !== void 0 && q.trim() !== "" ? await searchExerciseHistory(ctx.client, q, limit ?? 20) : await fetchExerciseHistoryList(ctx.client)).map((r) => ({
|
|
387
|
+
id: r.id,
|
|
388
|
+
title: r.title,
|
|
389
|
+
isCircuit: r.isCircuit ?? false,
|
|
390
|
+
units: exerciseUnits(r.param1Type, r.param2Type)
|
|
391
|
+
})), { hint: "Pass q to search by name, or limit to cap the list." });
|
|
392
|
+
}));
|
|
393
|
+
server.registerTool("athlete_exercise_history", {
|
|
394
|
+
title: "Exercise history + PRs",
|
|
395
|
+
description: "Per-exercise PRs and the dated session time-series (sets performed, estimated 1RM). Set raw:true for the untouched API object. Get the exercise id from athlete_exercises.",
|
|
396
|
+
inputSchema: {
|
|
397
|
+
exerciseId: idParam,
|
|
398
|
+
raw: z.boolean().optional()
|
|
399
|
+
},
|
|
400
|
+
annotations: READ
|
|
401
|
+
}, ({ exerciseId, raw }) => attempt(async () => {
|
|
402
|
+
const detail = await fetchExerciseHistoryDetail(ctx.client, toId(exerciseId), await userId());
|
|
403
|
+
return jsonResult(raw === true ? detail : presentExerciseHistory(detail));
|
|
404
|
+
}));
|
|
405
|
+
server.registerTool("athlete_personal_records", {
|
|
406
|
+
title: "Exercise personal records",
|
|
407
|
+
description: "Personal records for an exercise (reps/weight, strength-standard filters).",
|
|
408
|
+
inputSchema: { exerciseId: idParam },
|
|
409
|
+
annotations: READ
|
|
410
|
+
}, ({ exerciseId }) => attempt(async () => jsonResult(await fetchPersonalRecords(ctx.client, toId(exerciseId)))));
|
|
411
|
+
server.registerTool("athlete_exercise_stats", {
|
|
412
|
+
title: "Exercise stats (last performance + PR)",
|
|
413
|
+
description: "Last performance and PR for an exercise as of a date (YYYY-MM-DD, required by the API).",
|
|
414
|
+
inputSchema: {
|
|
415
|
+
exerciseId: idParam,
|
|
416
|
+
date: dateString
|
|
417
|
+
},
|
|
418
|
+
annotations: READ
|
|
419
|
+
}, ({ exerciseId, date }) => attempt(async () => jsonResult(await fetchExerciseStats(ctx.client, toId(exerciseId), await userId(), date))));
|
|
334
420
|
}
|
|
335
|
-
/**
|
|
336
|
-
function
|
|
337
|
-
|
|
338
|
-
|
|
421
|
+
/** The gated set-logging write. */
|
|
422
|
+
function registerLogTool(server, ctx) {
|
|
423
|
+
server.registerTool("athlete_log_set", {
|
|
424
|
+
title: "Log completed set results",
|
|
425
|
+
description: "Athlete-facing write: record entered results (reps/weight per set) for a saved workout set on a given day, marking the set completed. Writes to the athlete's (coach-visible) training log and shows in exercise history. Get savedWorkoutSetId + savedWorkoutSetExerciseId from athlete_workouts (raw:true). Requires confirmation (elicitation or confirm:true).",
|
|
426
|
+
inputSchema: {
|
|
427
|
+
...logSetArgsSchema.shape,
|
|
428
|
+
confirm: z.boolean().optional()
|
|
429
|
+
},
|
|
430
|
+
annotations: DESTRUCTIVE
|
|
431
|
+
}, ({ date, savedWorkoutSetId, results, confirm }, extra) => attempt(async () => {
|
|
432
|
+
if (!await confirmGate(server, extra.requestId, `Log results to saved workout set ${toId(savedWorkoutSetId)} on ${date}? This writes to your coach-visible training log.`, confirm)) return errorResult(NOT_CONFIRMED);
|
|
433
|
+
const mapped = results.map((r) => ({
|
|
434
|
+
savedWorkoutSetExerciseId: toId(r.savedWorkoutSetExerciseId),
|
|
435
|
+
sets: r.sets.map((s) => {
|
|
436
|
+
const slot = {};
|
|
437
|
+
if (s.param1 !== void 0) slot.param1 = s.param1;
|
|
438
|
+
if (s.param2 !== void 0) slot.param2 = s.param2;
|
|
439
|
+
return slot;
|
|
440
|
+
})
|
|
441
|
+
}));
|
|
442
|
+
return jsonResult(await logAthleteSet(ctx.client, {
|
|
443
|
+
date,
|
|
444
|
+
savedWorkoutSetId: toId(savedWorkoutSetId),
|
|
445
|
+
results: mapped
|
|
446
|
+
}));
|
|
447
|
+
}));
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Live tools over the logged-in user's own training (history, scheduled/completed workouts,
|
|
451
|
+
* PRs, working maxes), plus a gated set-logging write. The athlete user id is
|
|
452
|
+
* resolved once from /user/simple and reused across tools.
|
|
453
|
+
*/
|
|
454
|
+
function registerAthleteTrainingTools(server, ctx) {
|
|
455
|
+
let whoamiCache = null;
|
|
456
|
+
const whoami = async () => {
|
|
457
|
+
if (whoamiCache === null) {
|
|
458
|
+
const res = await ctx.client.request("GET", "/user/simple");
|
|
459
|
+
if (!res.ok || !isRecord(res.data)) throw new Error("Could not load /user/simple.");
|
|
460
|
+
whoamiCache = res.data;
|
|
461
|
+
}
|
|
462
|
+
return whoamiCache;
|
|
463
|
+
};
|
|
464
|
+
const userId = async () => {
|
|
465
|
+
const id = coerceInt((await whoami()).id);
|
|
466
|
+
if (id === null || id <= 0) throw new Error("Could not resolve athlete user id from /user/simple.");
|
|
467
|
+
return id;
|
|
468
|
+
};
|
|
469
|
+
registerProfileTools(server, ctx, whoami, userId);
|
|
470
|
+
registerExerciseTools$1(server, ctx, userId);
|
|
471
|
+
registerLogTool(server, ctx);
|
|
339
472
|
}
|
|
340
473
|
//#endregion
|
|
341
474
|
//#region src/tools/athletes.ts
|
|
@@ -486,7 +619,6 @@ const METRIC_KEYS = [
|
|
|
486
619
|
"readiness-athlete",
|
|
487
620
|
"lift-1rm-history",
|
|
488
621
|
"training-summary-athlete",
|
|
489
|
-
"training-summary-team",
|
|
490
622
|
"compliance-team",
|
|
491
623
|
"lift-progress-team",
|
|
492
624
|
"working-max-history"
|
|
@@ -495,58 +627,80 @@ const METRICS = {
|
|
|
495
627
|
"readiness-team": {
|
|
496
628
|
path: "/v5/analytics/readiness/teams",
|
|
497
629
|
scope: "team",
|
|
498
|
-
|
|
630
|
+
inputs: ["date"],
|
|
631
|
+
requires: ["date"]
|
|
499
632
|
},
|
|
500
633
|
"readiness-athlete": {
|
|
501
634
|
path: "/v5/analytics/readiness/users",
|
|
502
635
|
scope: "user",
|
|
503
|
-
|
|
636
|
+
inputs: ["dateStart", "dateEnd"],
|
|
637
|
+
requires: ["dateStart", "dateEnd"]
|
|
504
638
|
},
|
|
505
639
|
"lift-1rm-history": {
|
|
506
640
|
path: "/v5/analytics/lift-one-rep-max-history/users",
|
|
507
641
|
scope: "user",
|
|
508
|
-
|
|
509
|
-
"
|
|
510
|
-
"
|
|
511
|
-
"
|
|
512
|
-
"
|
|
642
|
+
inputs: [
|
|
643
|
+
"dateStart",
|
|
644
|
+
"dateEnd",
|
|
645
|
+
"exerciseId",
|
|
646
|
+
"useMetric"
|
|
647
|
+
],
|
|
648
|
+
requires: [
|
|
649
|
+
"dateStart",
|
|
650
|
+
"dateEnd",
|
|
651
|
+
"exerciseId"
|
|
513
652
|
]
|
|
514
653
|
},
|
|
515
654
|
"training-summary-athlete": {
|
|
516
655
|
path: "/v5/analytics/training-summary/users",
|
|
517
656
|
scope: "user",
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
"training-summary-team": {
|
|
521
|
-
path: "/v5/analytics/training-summary/teams",
|
|
522
|
-
scope: "team",
|
|
523
|
-
fields: ["date_start", "date_end"]
|
|
657
|
+
inputs: ["dateStart", "dateEnd"],
|
|
658
|
+
requires: ["dateStart", "dateEnd"]
|
|
524
659
|
},
|
|
525
660
|
"compliance-team": {
|
|
526
661
|
path: "/v5/analytics/compliance",
|
|
527
662
|
scope: "team",
|
|
528
|
-
|
|
663
|
+
inputs: ["dateStart", "dateEnd"],
|
|
664
|
+
requires: ["dateStart", "dateEnd"]
|
|
529
665
|
},
|
|
530
666
|
"lift-progress-team": {
|
|
531
667
|
path: "/v5/analytics/lift-progress/teams",
|
|
532
668
|
scope: "team",
|
|
533
|
-
|
|
534
|
-
"
|
|
535
|
-
"
|
|
536
|
-
"
|
|
669
|
+
inputs: [
|
|
670
|
+
"exerciseId",
|
|
671
|
+
"dateStart",
|
|
672
|
+
"dateEnd"
|
|
673
|
+
],
|
|
674
|
+
requires: [
|
|
675
|
+
"exerciseId",
|
|
676
|
+
"dateStart",
|
|
677
|
+
"dateEnd"
|
|
537
678
|
]
|
|
538
679
|
},
|
|
539
680
|
"working-max-history": {
|
|
540
681
|
path: "/v5/analytics/working-max-history/users",
|
|
541
682
|
scope: "user",
|
|
542
|
-
|
|
683
|
+
inputs: [
|
|
684
|
+
"exerciseId",
|
|
685
|
+
"dateStart",
|
|
686
|
+
"dateEnd",
|
|
687
|
+
"useMetric"
|
|
688
|
+
],
|
|
689
|
+
requires: ["exerciseId"]
|
|
543
690
|
}
|
|
544
691
|
};
|
|
692
|
+
const BODY_KEY = {
|
|
693
|
+
date: "date",
|
|
694
|
+
dateStart: "date_start",
|
|
695
|
+
dateEnd: "date_end",
|
|
696
|
+
exerciseId: "exercise_id",
|
|
697
|
+
useMetric: "use_metric"
|
|
698
|
+
};
|
|
545
699
|
/** Analytics report pulls. Read-only despite the POST verb, so ungated. */
|
|
546
700
|
function registerAnalyticsTools(server, ctx) {
|
|
547
701
|
server.registerTool("analytics_query", {
|
|
548
702
|
title: "Pull an analytics report",
|
|
549
|
-
description: "Read a TrainHeroic analytics report. `metric` picks the report (run analytics_categories for the catalog). Team metrics need teamId; athlete metrics need userIds.
|
|
703
|
+
description: "Read a TrainHeroic analytics report. `metric` picks the report (run analytics_categories for the catalog). Team metrics (readiness-team, compliance-team, lift-progress-team) need teamId; athlete metrics need userIds. readiness-team takes a single `date`; every other metric takes dateStart/dateEnd. lift-1rm-history, lift-progress-team, and working-max-history also need exerciseId. All dates are YYYY-MM-DD.",
|
|
550
704
|
inputSchema: {
|
|
551
705
|
metric: z.enum(METRIC_KEYS),
|
|
552
706
|
teamId: idParam.optional(),
|
|
@@ -560,6 +714,13 @@ function registerAnalyticsTools(server, ctx) {
|
|
|
560
714
|
annotations: READ
|
|
561
715
|
}, async ({ metric, teamId, userIds, exerciseId, date, dateStart, dateEnd, useMetric }) => {
|
|
562
716
|
const spec = METRICS[metric];
|
|
717
|
+
const inputs = {
|
|
718
|
+
date,
|
|
719
|
+
dateStart,
|
|
720
|
+
dateEnd,
|
|
721
|
+
exerciseId,
|
|
722
|
+
useMetric
|
|
723
|
+
};
|
|
563
724
|
const body = {};
|
|
564
725
|
if (spec.scope === "team") {
|
|
565
726
|
if (teamId === void 0) return errorResult(`${metric} needs teamId.`);
|
|
@@ -568,14 +729,13 @@ function registerAnalyticsTools(server, ctx) {
|
|
|
568
729
|
if (userIds === void 0 || userIds.length === 0) return errorResult(`${metric} needs userIds (one or more athlete ids).`);
|
|
569
730
|
body.user_ids = userIds.map((u) => String(toId(u)));
|
|
570
731
|
}
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
}
|
|
578
|
-
for (const field of spec.fields) if (provided[field] !== void 0) body[field] = provided[field];
|
|
732
|
+
const missing = spec.requires.filter((k) => inputs[k] === void 0);
|
|
733
|
+
if (missing.length > 0) return errorResult(`${metric} also needs: ${missing.join(", ")}.`);
|
|
734
|
+
for (const k of spec.inputs) {
|
|
735
|
+
const v = inputs[k];
|
|
736
|
+
if (v === void 0) continue;
|
|
737
|
+
body[BODY_KEY[k]] = k === "exerciseId" ? String(toId(v)) : v;
|
|
738
|
+
}
|
|
579
739
|
return apiCall(ctx, "POST", spec.path, { body }, "Narrow with a tighter date range or fewer athletes.");
|
|
580
740
|
});
|
|
581
741
|
}
|
|
@@ -891,4 +1051,4 @@ function registerMessagingTools(server, ctx) {
|
|
|
891
1051
|
registerWrites(server, ctx);
|
|
892
1052
|
}
|
|
893
1053
|
//#endregion
|
|
894
|
-
export { DEFAULT_RESULT_BUDGET, DESTRUCTIVE, NOT_CONFIRMED, READ, SYNC, apiCall, attempt, boundedSerialize, confirmGate, errorResult, idParam, jsonResult, registerAnalyticsTools, registerAthleteTools, registerExerciseTools, registerMessagingTools, registerReadTools, registerTeamTools, registerWorkoutTools, resultBudget, toId };
|
|
1054
|
+
export { DEFAULT_RESULT_BUDGET, DESTRUCTIVE, NOT_CONFIRMED, READ, SYNC, apiCall, attempt, boundedSerialize, confirmGate, errorResult, idParam, jsonResult, registerAnalyticsTools, registerAthleteTools, registerAthleteTrainingTools, registerExerciseTools, registerMessagingTools, registerReadTools, registerTeamTools, registerWorkoutTools, resultBudget, toId };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trainheroic-unofficial/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"@cfworker/json-schema": "^4.1.1",
|
|
26
26
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
27
27
|
"zod": "^4.4.3",
|
|
28
|
-
"@trainheroic-unofficial/dto": "0.
|
|
29
|
-
"@trainheroic-unofficial/js": "0.
|
|
28
|
+
"@trainheroic-unofficial/dto": "0.4.0",
|
|
29
|
+
"@trainheroic-unofficial/js": "0.4.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^26.0.0",
|