trainerroad-cli 0.1.1 → 0.3.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/CHANGELOG.md +10 -0
- package/README.md +60 -1
- package/package.json +13 -1
- package/src/cli.mjs +121 -21
- package/src/commands/auth.mjs +3 -2
- package/src/commands/discovery.mjs +9 -8
- package/src/commands/train-now.mjs +142 -0
- package/src/commands/workout-library.mjs +430 -0
- package/src/commands/workout-mutations.mjs +310 -0
- package/src/commands/workout-recommend.mjs +175 -0
- package/src/commands/workout-tools.mjs +388 -0
- package/src/lib/command-manifest.mjs +465 -25
- package/src/trainerroad-client.mjs +295 -1
|
@@ -2,112 +2,272 @@ export const COMMANDS = {
|
|
|
2
2
|
help: {
|
|
3
3
|
summary: "Show global help or command help.",
|
|
4
4
|
usage: [
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
5
|
+
"trainerroad-cli help",
|
|
6
|
+
"trainerroad-cli help <command>",
|
|
7
|
+
"trainerroad-cli <command> --help",
|
|
8
|
+
],
|
|
9
|
+
examples: [
|
|
10
|
+
"trainerroad-cli help future",
|
|
11
|
+
"trainerroad-cli help future --json",
|
|
8
12
|
],
|
|
9
13
|
},
|
|
10
14
|
discover: {
|
|
11
15
|
summary: "Agent-oriented command discovery with progressive disclosure levels.",
|
|
12
16
|
usage: [
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
17
|
+
"trainerroad-cli discover",
|
|
18
|
+
"trainerroad-cli discover --level 1|2|3 [--json]",
|
|
19
|
+
"trainerroad-cli discover --command future --level 3 --json",
|
|
20
|
+
],
|
|
21
|
+
examples: [
|
|
22
|
+
"trainerroad-cli discover --level 1",
|
|
23
|
+
"trainerroad-cli discover --command workout-library --level 3 --json",
|
|
16
24
|
],
|
|
17
25
|
},
|
|
18
26
|
capabilities: {
|
|
19
27
|
summary: "Show supported auth/data capabilities (private + public modes).",
|
|
20
|
-
usage: ["
|
|
28
|
+
usage: ["trainerroad-cli capabilities [--json]"],
|
|
29
|
+
examples: ["trainerroad-cli capabilities --json"],
|
|
21
30
|
},
|
|
22
31
|
login: {
|
|
23
32
|
summary: "Authenticate and persist cookie session for private data access.",
|
|
24
33
|
usage: [
|
|
25
|
-
"
|
|
26
|
-
"
|
|
34
|
+
"trainerroad-cli login --username <username> --password <password> [--return-path /app/career/<username>]",
|
|
35
|
+
"trainerroad-cli login --username <username> --password-stdin",
|
|
36
|
+
],
|
|
37
|
+
examples: [
|
|
38
|
+
"trainerroad-cli login --username quinnsprouse --password-stdin",
|
|
39
|
+
"TR_PASSWORD='<password>' trainerroad-cli login --username quinnsprouse",
|
|
27
40
|
],
|
|
28
41
|
},
|
|
29
42
|
whoami: {
|
|
30
43
|
summary: "Fetch authenticated member profile info (`/app/api/member-info`).",
|
|
31
|
-
usage: ["
|
|
44
|
+
usage: ["trainerroad-cli whoami [--json]"],
|
|
45
|
+
examples: ["trainerroad-cli whoami --json"],
|
|
32
46
|
},
|
|
33
47
|
timeline: {
|
|
34
48
|
summary: "Get profile summary (private full timeline or public TSS-derived summary).",
|
|
35
49
|
usage: [
|
|
36
|
-
"
|
|
37
|
-
|
|
50
|
+
"trainerroad-cli timeline [--target <username>] [--public] [--full] [--json]",
|
|
51
|
+
],
|
|
52
|
+
examples: [
|
|
53
|
+
"trainerroad-cli timeline --json",
|
|
54
|
+
"trainerroad-cli timeline --target quinnsprouse --public --json",
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
"train-now": {
|
|
58
|
+
summary: "Fetch TrainerRoad AI suggested workouts (TrainNow) for a target duration (private mode).",
|
|
59
|
+
usage: [
|
|
60
|
+
"trainerroad-cli train-now [--duration <minutes>] [--num-suggestions <count>] [--category climbing|endurance|attacking] [--json|--jsonl]",
|
|
61
|
+
],
|
|
62
|
+
examples: [
|
|
63
|
+
"trainerroad-cli train-now --duration 60 --json",
|
|
64
|
+
"trainerroad-cli train-now --duration 90 --category endurance --json",
|
|
38
65
|
],
|
|
39
66
|
},
|
|
40
67
|
events: {
|
|
41
68
|
summary: "Show calendar events/races from timeline (private mode).",
|
|
42
69
|
usage: [
|
|
43
|
-
"
|
|
70
|
+
"trainerroad-cli events [--full] [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--type <value>] [--contains <text>] [--min-tss <number>] [--max-tss <number>] [--sort date|date-desc|name|name-desc|tss|tss-desc] [--result-limit <count>] [--fields a,b] [--records-only] [--json|--jsonl]",
|
|
71
|
+
],
|
|
72
|
+
examples: [
|
|
73
|
+
"trainerroad-cli events --from 2026-03-01 --to 2026-03-31 --json",
|
|
74
|
+
"trainerroad-cli events --contains gravel --sort date --result-limit 10 --json",
|
|
44
75
|
],
|
|
45
76
|
},
|
|
46
77
|
annotations: {
|
|
47
78
|
summary: "Show timeline annotations (time off, notes, illness/injury markers) (private mode).",
|
|
48
79
|
usage: [
|
|
49
|
-
"
|
|
80
|
+
"trainerroad-cli annotations [--full] [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--type <value>] [--contains <text>] [--sort date|date-desc|name|name-desc] [--result-limit <count>] [--fields a,b] [--records-only] [--json|--jsonl]",
|
|
81
|
+
],
|
|
82
|
+
examples: [
|
|
83
|
+
"trainerroad-cli annotations --from 2026-01-01 --json",
|
|
84
|
+
"trainerroad-cli annotations --contains injury --sort date-desc --json",
|
|
50
85
|
],
|
|
51
86
|
},
|
|
52
87
|
levels: {
|
|
53
88
|
summary: "Show progression levels by zone (private mode).",
|
|
54
89
|
usage: [
|
|
55
|
-
"
|
|
90
|
+
"trainerroad-cli levels [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--type <zone|progressionId>] [--contains <text>] [--sort name|name-desc|date|date-desc] [--result-limit <count>] [--fields a,b] [--records-only] [--json|--jsonl]",
|
|
91
|
+
],
|
|
92
|
+
examples: [
|
|
93
|
+
"trainerroad-cli levels --json",
|
|
94
|
+
"trainerroad-cli levels --type endurance --json",
|
|
56
95
|
],
|
|
57
96
|
},
|
|
58
97
|
plan: {
|
|
59
98
|
summary: "Show training plan data (current plan, phases, or all plans) (private mode).",
|
|
60
99
|
usage: [
|
|
61
|
-
"
|
|
100
|
+
"trainerroad-cli plan [--view current|phases|plans] [--full] [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--type <value>] [--contains <text>] [--sort date|date-desc|name|name-desc] [--result-limit <count>] [--fields a,b] [--records-only] [--json|--jsonl]",
|
|
101
|
+
],
|
|
102
|
+
examples: [
|
|
103
|
+
"trainerroad-cli plan --view current --json",
|
|
104
|
+
"trainerroad-cli plan --view phases --json",
|
|
62
105
|
],
|
|
63
106
|
},
|
|
64
107
|
"weight-history": {
|
|
65
108
|
summary: "Show historical body-weight entries (private mode).",
|
|
66
109
|
usage: [
|
|
67
|
-
"
|
|
110
|
+
"trainerroad-cli weight-history [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--contains <text>] [--sort date|date-desc] [--result-limit <count>] [--fields a,b] [--records-only] [--json|--jsonl]",
|
|
111
|
+
],
|
|
112
|
+
examples: [
|
|
113
|
+
"trainerroad-cli weight-history --json",
|
|
114
|
+
"trainerroad-cli weight-history --from 2026-01-01 --sort date-desc --json",
|
|
68
115
|
],
|
|
69
116
|
},
|
|
70
117
|
today: {
|
|
71
118
|
summary: "Show today's planned/completed activity for private or public profile mode.",
|
|
72
119
|
usage: [
|
|
73
|
-
"
|
|
120
|
+
"trainerroad-cli today [--date YYYY-MM-DD] [--target <username>] [--public] [--details] [--type <value>] [--contains <text>] [--min-tss <number>] [--max-tss <number>] [--sort date|date-desc|tss|tss-desc] [--result-limit <count>] [--fields a,b] [--records-only] [--json|--jsonl]",
|
|
121
|
+
],
|
|
122
|
+
examples: [
|
|
123
|
+
"trainerroad-cli today --json",
|
|
124
|
+
"trainerroad-cli today --target quinnsprouse --public --tz America/New_York --json",
|
|
74
125
|
],
|
|
75
126
|
},
|
|
76
127
|
future: {
|
|
77
128
|
summary: "Show future plan data for private or public profile mode.",
|
|
78
129
|
usage: [
|
|
79
|
-
"
|
|
130
|
+
"trainerroad-cli future [--days <count>] [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--target <username>] [--public] [--details] [--type <value>] [--contains <text>] [--min-tss <number>] [--max-tss <number>] [--sort date|date-desc|tss|tss-desc] [--result-limit <count>] [--fields a,b] [--records-only] [--json|--jsonl]",
|
|
131
|
+
],
|
|
132
|
+
examples: [
|
|
133
|
+
"trainerroad-cli future --days 30 --json",
|
|
134
|
+
"trainerroad-cli future --from 2026-03-01 --to 2026-03-31 --fields id,title,tss,date --json",
|
|
80
135
|
],
|
|
81
136
|
},
|
|
82
137
|
past: {
|
|
83
138
|
summary: "Show past activity data for private or public profile mode.",
|
|
84
139
|
usage: [
|
|
85
|
-
"
|
|
140
|
+
"trainerroad-cli past [--days <count>] [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit <count>] [--target <username>] [--public] [--details] [--type <value>] [--contains <text>] [--min-tss <number>] [--max-tss <number>] [--sort date|date-desc|tss|tss-desc] [--result-limit <count>] [--fields a,b] [--records-only] [--json|--jsonl]",
|
|
141
|
+
],
|
|
142
|
+
examples: [
|
|
143
|
+
"trainerroad-cli past --days 30 --json",
|
|
144
|
+
"trainerroad-cli past --days 90 --min-tss 80 --sort tss-desc --result-limit 20 --jsonl",
|
|
86
145
|
],
|
|
87
146
|
},
|
|
88
147
|
ftp: {
|
|
89
148
|
summary: "Show FTP snapshot and FTP history (private or public mode).",
|
|
90
149
|
usage: [
|
|
91
|
-
"
|
|
150
|
+
"trainerroad-cli ftp [--target <username>] [--public] [--history-limit <count>] [--json|--jsonl]",
|
|
151
|
+
],
|
|
152
|
+
examples: [
|
|
153
|
+
"trainerroad-cli ftp --json",
|
|
154
|
+
"trainerroad-cli ftp --target quinnsprouse --public --history-limit 12 --json",
|
|
92
155
|
],
|
|
93
156
|
},
|
|
94
157
|
"ftp-prediction": {
|
|
95
158
|
summary: "Show AI FTP detection eligibility/status and progression impact (private mode).",
|
|
96
|
-
usage: ["
|
|
159
|
+
usage: ["trainerroad-cli ftp-prediction [--json]"],
|
|
160
|
+
examples: ["trainerroad-cli ftp-prediction --json"],
|
|
97
161
|
},
|
|
98
162
|
"power-ranking": {
|
|
99
163
|
summary: "Show best-power percentile ranking by duration (private mode).",
|
|
100
|
-
usage: ["
|
|
164
|
+
usage: ["trainerroad-cli power-ranking [--json|--jsonl]"],
|
|
165
|
+
examples: ["trainerroad-cli power-ranking --json"],
|
|
101
166
|
},
|
|
102
167
|
"power-records": {
|
|
103
168
|
summary: "Show date-range personal power records from TrainerRoad PR endpoint (private mode).",
|
|
104
169
|
usage: [
|
|
105
|
-
"
|
|
170
|
+
"trainerroad-cli power-records [--start-date YYYY-MM-DD] [--end-date YYYY-MM-DD] [--row-type 100|101] [--indoor-only true|false] [--limit <count>] [--full] [--json|--jsonl]",
|
|
171
|
+
],
|
|
172
|
+
examples: [
|
|
173
|
+
"trainerroad-cli power-records --start-date 2026-01-01 --end-date 2026-03-01 --json",
|
|
174
|
+
"trainerroad-cli power-records --row-type 101 --indoor-only true --limit 20 --jsonl",
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
"workout-library": {
|
|
178
|
+
summary: "Search the TrainerRoad workout library with agent-friendly filters (private mode).",
|
|
179
|
+
usage: [
|
|
180
|
+
"trainerroad-cli workout-library [--search <text>] [--zone <name>|--zone-id <id>] [--profile <name>|--profile-id <id>] [--outside true|false] [--has-instructions true|false] [--min-duration <minutes>] [--max-duration <minutes>] [--min-tss <number>] [--max-tss <number>] [--min-level <number>] [--max-level <number>] [--sort level|level-desc|duration|duration-desc|tss|tss-desc|name|name-desc] [--limit <count>] [--page-size <count>] [--json|--jsonl]",
|
|
181
|
+
],
|
|
182
|
+
examples: [
|
|
183
|
+
"trainerroad-cli workout-library --zone Endurance --profile \"Sustained Power\" --min-duration 45 --max-duration 75 --json",
|
|
184
|
+
"trainerroad-cli workout-library --search Baxter --limit 5 --json",
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
"workout-recommend": {
|
|
188
|
+
summary: "Recommend library workouts by ranking candidates against target duration/level/TSS (private mode).",
|
|
189
|
+
usage: [
|
|
190
|
+
"trainerroad-cli workout-recommend [--search <text>] [--zone <name>|--zone-id <id>] [--profile <name>|--profile-id <id>] [--outside true|false] [--has-instructions true|false] [--min-duration <minutes>] [--max-duration <minutes>] [--min-tss <number>] [--max-tss <number>] [--min-level <number>] [--max-level <number>] [--target-duration <minutes>] [--target-tss <number>] [--target-level <number>] [--count <count>] [--candidate-limit <count>] [--page-size <count>] [--sort level|level-desc|duration|duration-desc|tss|tss-desc|name|name-desc] [--json|--jsonl]",
|
|
191
|
+
],
|
|
192
|
+
examples: [
|
|
193
|
+
"trainerroad-cli workout-recommend --zone Endurance --profile \"Sustained Power\" --target-duration 60 --target-level 1.0 --count 3 --json",
|
|
194
|
+
"trainerroad-cli workout-recommend --search threshold --target-duration 90 --count 5 --json",
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
"workout-details": {
|
|
198
|
+
summary: "Fetch detailed workout-library metadata for a workout ID (private mode).",
|
|
199
|
+
usage: [
|
|
200
|
+
"trainerroad-cli workout-details --id <workout-id> [--include-chart true|false] [--chart-point-limit <count>] [--json|--jsonl]",
|
|
201
|
+
],
|
|
202
|
+
examples: [
|
|
203
|
+
"trainerroad-cli workout-details --id 18128 --json",
|
|
204
|
+
"trainerroad-cli workout-details --id 18128 --include-chart --chart-point-limit 50 --json",
|
|
205
|
+
],
|
|
206
|
+
},
|
|
207
|
+
"add-workout": {
|
|
208
|
+
summary: "Add a library workout to the calendar on a target date (private mode; reconciles flaky API responses).",
|
|
209
|
+
usage: [
|
|
210
|
+
"trainerroad-cli add-workout --workout-id <workout-id> --date YYYY-MM-DD [--outside true|false] [--dry-run] [--json|--jsonl]",
|
|
211
|
+
],
|
|
212
|
+
examples: [
|
|
213
|
+
"trainerroad-cli add-workout --workout-id 18128 --date 2026-03-16 --dry-run",
|
|
214
|
+
"trainerroad-cli add-workout --workout-id 18128 --date 2026-03-16 --json",
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
"copy-workout": {
|
|
218
|
+
summary: "Copy an existing planned workout to another date (private mode).",
|
|
219
|
+
usage: [
|
|
220
|
+
"trainerroad-cli copy-workout --id <planned-activity-id> --date YYYY-MM-DD [--dry-run] [--json|--jsonl]",
|
|
221
|
+
],
|
|
222
|
+
examples: [
|
|
223
|
+
"trainerroad-cli copy-workout --id 123456 --date 2026-03-16 --dry-run",
|
|
224
|
+
"trainerroad-cli copy-workout --id 123456 --date 2026-03-16 --json",
|
|
225
|
+
],
|
|
226
|
+
},
|
|
227
|
+
"workout-alternates": {
|
|
228
|
+
summary: "List alternate workout options for a planned workout (private mode).",
|
|
229
|
+
usage: [
|
|
230
|
+
"trainerroad-cli workout-alternates --id <planned-activity-id> [--category similar|easier|harder|longer|shorter] [--json|--jsonl]",
|
|
231
|
+
],
|
|
232
|
+
examples: [
|
|
233
|
+
"trainerroad-cli workout-alternates --id 123456 --json",
|
|
234
|
+
"trainerroad-cli workout-alternates --id 123456 --category easier --json",
|
|
235
|
+
],
|
|
236
|
+
},
|
|
237
|
+
"move-workout": {
|
|
238
|
+
summary: "Move a planned workout to a different date (private mode).",
|
|
239
|
+
usage: [
|
|
240
|
+
"trainerroad-cli move-workout --id <planned-activity-id> --to YYYY-MM-DD [--dry-run] [--json|--jsonl]",
|
|
241
|
+
],
|
|
242
|
+
examples: [
|
|
243
|
+
"trainerroad-cli move-workout --id 123456 --to 2026-03-13 --dry-run",
|
|
244
|
+
"trainerroad-cli move-workout --id 123456 --to 2026-03-13 --json",
|
|
245
|
+
],
|
|
246
|
+
},
|
|
247
|
+
"replace-workout": {
|
|
248
|
+
summary: "Replace a planned workout with a specific alternate workout ID (private mode).",
|
|
249
|
+
usage: [
|
|
250
|
+
"trainerroad-cli replace-workout --id <planned-activity-id> --alternate-id <workout-id> [--update-duration true|false] [--dry-run] [--json|--jsonl]",
|
|
251
|
+
],
|
|
252
|
+
examples: [
|
|
253
|
+
"trainerroad-cli replace-workout --id 123456 --alternate-id 18128 --dry-run",
|
|
254
|
+
"trainerroad-cli replace-workout --id 123456 --alternate-id 18128 --json",
|
|
255
|
+
],
|
|
256
|
+
},
|
|
257
|
+
"switch-workout": {
|
|
258
|
+
summary: "Switch a planned workout between inside and outside variants (private mode).",
|
|
259
|
+
usage: [
|
|
260
|
+
"trainerroad-cli switch-workout --id <planned-activity-id> --mode inside|outside [--dry-run] [--json|--jsonl]",
|
|
261
|
+
],
|
|
262
|
+
examples: [
|
|
263
|
+
"trainerroad-cli switch-workout --id 123456 --mode outside --dry-run",
|
|
264
|
+
"trainerroad-cli switch-workout --id 123456 --mode outside --json",
|
|
106
265
|
],
|
|
107
266
|
},
|
|
108
267
|
logout: {
|
|
109
268
|
summary: "Clear local persisted session.",
|
|
110
|
-
usage: ["
|
|
269
|
+
usage: ["trainerroad-cli logout"],
|
|
270
|
+
examples: ["trainerroad-cli logout"],
|
|
111
271
|
},
|
|
112
272
|
};
|
|
113
273
|
|
|
@@ -116,6 +276,7 @@ export const PROJECT_NOTICE = "Unofficial tool. Not affiliated with or endorsed
|
|
|
116
276
|
export const GLOBAL_NOTES = [
|
|
117
277
|
PROJECT_NOTICE,
|
|
118
278
|
"Environment: TR_USERNAME, TR_PASSWORD, TR_SESSION_FILE, TR_TIMEZONE",
|
|
279
|
+
"Non-interactive by default: pass flags or stdin; commands do not prompt.",
|
|
119
280
|
"Timezone: --tz <IANA timezone> (for example America/New_York).",
|
|
120
281
|
"Output modes: default JSON, --json, --jsonl, --output <path>",
|
|
121
282
|
"Session file default: .trainerroad/session.json",
|
|
@@ -123,6 +284,7 @@ export const GLOBAL_NOTES = [
|
|
|
123
284
|
"Public mode: username-based endpoint (`/app/api/tss/{username}`) with limited detail.",
|
|
124
285
|
"Agent filters: --from --to --type --contains --min-tss --max-tss --sort --result-limit --fields",
|
|
125
286
|
"Agent output: --records-only",
|
|
287
|
+
"Write commands: use --dry-run to preview calendar mutations before applying them.",
|
|
126
288
|
];
|
|
127
289
|
|
|
128
290
|
export const AGENT_FILTER_OPTIONS = [
|
|
@@ -156,6 +318,160 @@ export const FILTERABLE_COMMANDS = new Set([
|
|
|
156
318
|
"weight-history",
|
|
157
319
|
]);
|
|
158
320
|
|
|
321
|
+
export const COMMAND_REQUIRED_FLAGS = {
|
|
322
|
+
"workout-details": ["id"],
|
|
323
|
+
"add-workout": ["workout-id", "date"],
|
|
324
|
+
"copy-workout": ["id", "date"],
|
|
325
|
+
"workout-alternates": ["id"],
|
|
326
|
+
"move-workout": ["id", "to"],
|
|
327
|
+
"replace-workout": ["id", "alternate-id"],
|
|
328
|
+
"switch-workout": ["id", "mode"],
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
export const FLAG_DETAILS = {
|
|
332
|
+
help: { description: "Show command help and exit." },
|
|
333
|
+
output: { placeholder: "<path>", description: "Write command output to a file instead of stdout." },
|
|
334
|
+
json: { description: "Emit machine-readable JSON output." },
|
|
335
|
+
jsonl: { description: "Emit newline-delimited JSON records." },
|
|
336
|
+
"session-file": {
|
|
337
|
+
placeholder: "<path>",
|
|
338
|
+
description: "Override the persisted session file path.",
|
|
339
|
+
},
|
|
340
|
+
username: { placeholder: "<username>", description: "TrainerRoad account username." },
|
|
341
|
+
password: {
|
|
342
|
+
placeholder: "<password>",
|
|
343
|
+
description: "TrainerRoad account password. Prefer --password-stdin or TR_PASSWORD.",
|
|
344
|
+
},
|
|
345
|
+
"password-stdin": {
|
|
346
|
+
description: "Read the password from stdin so the command stays non-interactive and shell-history safe.",
|
|
347
|
+
},
|
|
348
|
+
"return-path": {
|
|
349
|
+
placeholder: "<path>",
|
|
350
|
+
description: "Override the login ReturnUrl used during the auth flow.",
|
|
351
|
+
},
|
|
352
|
+
level: { placeholder: "1|2|3", description: "Discovery detail level." },
|
|
353
|
+
command: { placeholder: "<command>", description: "Limit discovery/help output to one command." },
|
|
354
|
+
target: { placeholder: "<username>", description: "Public TrainerRoad profile username to query." },
|
|
355
|
+
public: {
|
|
356
|
+
description: "Force public-mode requests even when an authenticated session exists.",
|
|
357
|
+
},
|
|
358
|
+
full: { description: "Return fuller upstream payload data when the command supports it." },
|
|
359
|
+
duration: { placeholder: "<minutes>", description: "Requested workout duration in minutes." },
|
|
360
|
+
"num-suggestions": {
|
|
361
|
+
placeholder: "<count>",
|
|
362
|
+
description: "Maximum TrainNow suggestions to request.",
|
|
363
|
+
},
|
|
364
|
+
category: {
|
|
365
|
+
placeholder: "<value>",
|
|
366
|
+
description: "Command-specific category filter (for example easier, endurance, or outside).",
|
|
367
|
+
},
|
|
368
|
+
from: { placeholder: "YYYY-MM-DD", description: "Inclusive lower date bound." },
|
|
369
|
+
to: { placeholder: "YYYY-MM-DD", description: "Inclusive upper date bound or move target date." },
|
|
370
|
+
type: { placeholder: "<value>", description: "Command-specific record type filter." },
|
|
371
|
+
contains: { placeholder: "<text>", description: "Case-insensitive substring filter." },
|
|
372
|
+
"min-tss": { placeholder: "<number>", description: "Minimum TSS threshold." },
|
|
373
|
+
"max-tss": { placeholder: "<number>", description: "Maximum TSS threshold." },
|
|
374
|
+
sort: { placeholder: "<mode>", description: "Command-specific sort mode." },
|
|
375
|
+
"result-limit": { placeholder: "<count>", description: "Limit records after filters are applied." },
|
|
376
|
+
fields: { placeholder: "a,b,c", description: "Project record fields for leaner downstream payloads." },
|
|
377
|
+
"records-only": {
|
|
378
|
+
description: "Return only the envelope and records arrays when supported.",
|
|
379
|
+
},
|
|
380
|
+
view: { placeholder: "current|phases|plans", description: "Plan view to return." },
|
|
381
|
+
date: { placeholder: "YYYY-MM-DD", description: "Target calendar date." },
|
|
382
|
+
details: { description: "Include additional workout/activity detail in the payload." },
|
|
383
|
+
days: { placeholder: "<count>", description: "Relative day window size." },
|
|
384
|
+
limit: { placeholder: "<count>", description: "Upstream or record limit for the command." },
|
|
385
|
+
"history-limit": {
|
|
386
|
+
placeholder: "<count>",
|
|
387
|
+
description: "Maximum FTP history entries to include.",
|
|
388
|
+
},
|
|
389
|
+
"start-date": { placeholder: "YYYY-MM-DD", description: "Personal records window start date." },
|
|
390
|
+
"end-date": { placeholder: "YYYY-MM-DD", description: "Personal records window end date." },
|
|
391
|
+
"row-type": {
|
|
392
|
+
placeholder: "100|101",
|
|
393
|
+
description: "TrainerRoad PR row type to query.",
|
|
394
|
+
},
|
|
395
|
+
"indoor-only": {
|
|
396
|
+
placeholder: "true|false",
|
|
397
|
+
description: "Restrict personal-record queries to indoor rides.",
|
|
398
|
+
},
|
|
399
|
+
slot: {
|
|
400
|
+
placeholder: "<value>",
|
|
401
|
+
description: "Reserved upstream query slot used by the power-records endpoint.",
|
|
402
|
+
},
|
|
403
|
+
search: { placeholder: "<text>", description: "Free-text workout search query." },
|
|
404
|
+
zone: { placeholder: "<name>", description: "Workout zone name filter." },
|
|
405
|
+
"zone-id": { placeholder: "<id>", description: "Workout zone numeric identifier." },
|
|
406
|
+
profile: { placeholder: "<name>", description: "Workout profile name filter." },
|
|
407
|
+
"profile-id": { placeholder: "<id>", description: "Workout profile numeric identifier." },
|
|
408
|
+
outside: {
|
|
409
|
+
placeholder: "true|false",
|
|
410
|
+
description: "Filter or create the outside workout variant where supported.",
|
|
411
|
+
},
|
|
412
|
+
"has-instructions": {
|
|
413
|
+
placeholder: "true|false",
|
|
414
|
+
description: "Filter workouts by whether text instructions exist.",
|
|
415
|
+
},
|
|
416
|
+
"min-duration": { placeholder: "<minutes>", description: "Minimum workout duration." },
|
|
417
|
+
"max-duration": { placeholder: "<minutes>", description: "Maximum workout duration." },
|
|
418
|
+
"min-level": { placeholder: "<number>", description: "Minimum workout progression level." },
|
|
419
|
+
"max-level": { placeholder: "<number>", description: "Maximum workout progression level." },
|
|
420
|
+
"target-duration": {
|
|
421
|
+
placeholder: "<minutes>",
|
|
422
|
+
description: "Target duration used when ranking recommended workouts.",
|
|
423
|
+
},
|
|
424
|
+
"target-tss": {
|
|
425
|
+
placeholder: "<number>",
|
|
426
|
+
description: "Target TSS used when ranking recommended workouts.",
|
|
427
|
+
},
|
|
428
|
+
"target-level": {
|
|
429
|
+
placeholder: "<number>",
|
|
430
|
+
description: "Target progression level used when ranking recommended workouts.",
|
|
431
|
+
},
|
|
432
|
+
count: { placeholder: "<count>", description: "Number of recommendations to return." },
|
|
433
|
+
"candidate-limit": {
|
|
434
|
+
placeholder: "<count>",
|
|
435
|
+
description: "Maximum candidate workouts to score before ranking.",
|
|
436
|
+
},
|
|
437
|
+
"page-size": {
|
|
438
|
+
placeholder: "<count>",
|
|
439
|
+
description: "Upstream workout library page size.",
|
|
440
|
+
},
|
|
441
|
+
id: { placeholder: "<id>", description: "Workout, planned activity, or record identifier." },
|
|
442
|
+
"include-chart": {
|
|
443
|
+
placeholder: "true|false",
|
|
444
|
+
description: "Include workout chart/sample data in workout-details.",
|
|
445
|
+
},
|
|
446
|
+
"chart-point-limit": {
|
|
447
|
+
placeholder: "<count>",
|
|
448
|
+
description: "Maximum chart points returned with --include-chart.",
|
|
449
|
+
},
|
|
450
|
+
"workout-id": {
|
|
451
|
+
placeholder: "<workout-id>",
|
|
452
|
+
description: "TrainerRoad workout library identifier.",
|
|
453
|
+
},
|
|
454
|
+
"dry-run": {
|
|
455
|
+
description: "Preview the write command and exit without mutating the calendar.",
|
|
456
|
+
},
|
|
457
|
+
"alternate-id": {
|
|
458
|
+
placeholder: "<workout-id>",
|
|
459
|
+
description: "Alternate workout ID to apply during replace-workout.",
|
|
460
|
+
},
|
|
461
|
+
"update-duration": {
|
|
462
|
+
placeholder: "true|false",
|
|
463
|
+
description: "Let TrainerRoad update the duration during replace-workout.",
|
|
464
|
+
},
|
|
465
|
+
mode: {
|
|
466
|
+
placeholder: "inside|outside",
|
|
467
|
+
description: "Target workout delivery mode for switch-workout.",
|
|
468
|
+
},
|
|
469
|
+
tz: {
|
|
470
|
+
placeholder: "<IANA timezone>",
|
|
471
|
+
description: "Override local-day bucketing (defaults to TR_TIMEZONE or system timezone).",
|
|
472
|
+
},
|
|
473
|
+
};
|
|
474
|
+
|
|
159
475
|
function trimFlagPrefix(flag) {
|
|
160
476
|
return String(flag ?? "").replace(/^--/, "").trim();
|
|
161
477
|
}
|
|
@@ -178,6 +494,7 @@ const SHARED_FLAGS = {
|
|
|
178
494
|
jsonAndJsonl: ["json", "jsonl"],
|
|
179
495
|
agentFilters: AGENT_FILTER_OPTIONS.map((option) => trimFlagPrefix(option.flag)),
|
|
180
496
|
agentOutput: AGENT_OUTPUT_OPTIONS.map((option) => trimFlagPrefix(option.flag)),
|
|
497
|
+
writeSafety: ["dry-run"],
|
|
181
498
|
};
|
|
182
499
|
|
|
183
500
|
export const COMMAND_FLAG_ALLOWLIST = {
|
|
@@ -212,6 +529,14 @@ export const COMMAND_FLAG_ALLOWLIST = {
|
|
|
212
529
|
SHARED_FLAGS.publicProfile,
|
|
213
530
|
["full"],
|
|
214
531
|
),
|
|
532
|
+
"train-now": mergeFlagGroups(
|
|
533
|
+
SHARED_FLAGS.help,
|
|
534
|
+
SHARED_FLAGS.output,
|
|
535
|
+
SHARED_FLAGS.jsonAndJsonl,
|
|
536
|
+
SHARED_FLAGS.session,
|
|
537
|
+
SHARED_FLAGS.credentials,
|
|
538
|
+
["duration", "num-suggestions", "category"],
|
|
539
|
+
),
|
|
215
540
|
events: mergeFlagGroups(
|
|
216
541
|
SHARED_FLAGS.help,
|
|
217
542
|
SHARED_FLAGS.output,
|
|
@@ -324,5 +649,120 @@ export const COMMAND_FLAG_ALLOWLIST = {
|
|
|
324
649
|
SHARED_FLAGS.credentials,
|
|
325
650
|
["start-date", "end-date", "row-type", "indoor-only", "slot", "limit", "full"],
|
|
326
651
|
),
|
|
652
|
+
"workout-library": mergeFlagGroups(
|
|
653
|
+
SHARED_FLAGS.help,
|
|
654
|
+
SHARED_FLAGS.output,
|
|
655
|
+
SHARED_FLAGS.jsonAndJsonl,
|
|
656
|
+
SHARED_FLAGS.session,
|
|
657
|
+
SHARED_FLAGS.credentials,
|
|
658
|
+
[
|
|
659
|
+
"search",
|
|
660
|
+
"zone",
|
|
661
|
+
"zone-id",
|
|
662
|
+
"profile",
|
|
663
|
+
"profile-id",
|
|
664
|
+
"outside",
|
|
665
|
+
"has-instructions",
|
|
666
|
+
"min-duration",
|
|
667
|
+
"max-duration",
|
|
668
|
+
"min-tss",
|
|
669
|
+
"max-tss",
|
|
670
|
+
"min-level",
|
|
671
|
+
"max-level",
|
|
672
|
+
"sort",
|
|
673
|
+
"limit",
|
|
674
|
+
"page-size",
|
|
675
|
+
],
|
|
676
|
+
),
|
|
677
|
+
"workout-recommend": mergeFlagGroups(
|
|
678
|
+
SHARED_FLAGS.help,
|
|
679
|
+
SHARED_FLAGS.output,
|
|
680
|
+
SHARED_FLAGS.jsonAndJsonl,
|
|
681
|
+
SHARED_FLAGS.session,
|
|
682
|
+
SHARED_FLAGS.credentials,
|
|
683
|
+
[
|
|
684
|
+
"search",
|
|
685
|
+
"zone",
|
|
686
|
+
"zone-id",
|
|
687
|
+
"profile",
|
|
688
|
+
"profile-id",
|
|
689
|
+
"outside",
|
|
690
|
+
"has-instructions",
|
|
691
|
+
"min-duration",
|
|
692
|
+
"max-duration",
|
|
693
|
+
"min-tss",
|
|
694
|
+
"max-tss",
|
|
695
|
+
"min-level",
|
|
696
|
+
"max-level",
|
|
697
|
+
"target-duration",
|
|
698
|
+
"target-tss",
|
|
699
|
+
"target-level",
|
|
700
|
+
"count",
|
|
701
|
+
"candidate-limit",
|
|
702
|
+
"page-size",
|
|
703
|
+
"sort",
|
|
704
|
+
],
|
|
705
|
+
),
|
|
706
|
+
"workout-details": mergeFlagGroups(
|
|
707
|
+
SHARED_FLAGS.help,
|
|
708
|
+
SHARED_FLAGS.output,
|
|
709
|
+
SHARED_FLAGS.jsonAndJsonl,
|
|
710
|
+
SHARED_FLAGS.session,
|
|
711
|
+
SHARED_FLAGS.credentials,
|
|
712
|
+
["id", "include-chart", "chart-point-limit"],
|
|
713
|
+
),
|
|
714
|
+
"add-workout": mergeFlagGroups(
|
|
715
|
+
SHARED_FLAGS.help,
|
|
716
|
+
SHARED_FLAGS.output,
|
|
717
|
+
SHARED_FLAGS.jsonAndJsonl,
|
|
718
|
+
SHARED_FLAGS.session,
|
|
719
|
+
SHARED_FLAGS.credentials,
|
|
720
|
+
SHARED_FLAGS.writeSafety,
|
|
721
|
+
["workout-id", "date", "outside"],
|
|
722
|
+
),
|
|
723
|
+
"copy-workout": mergeFlagGroups(
|
|
724
|
+
SHARED_FLAGS.help,
|
|
725
|
+
SHARED_FLAGS.output,
|
|
726
|
+
SHARED_FLAGS.jsonAndJsonl,
|
|
727
|
+
SHARED_FLAGS.session,
|
|
728
|
+
SHARED_FLAGS.credentials,
|
|
729
|
+
SHARED_FLAGS.writeSafety,
|
|
730
|
+
["id", "date"],
|
|
731
|
+
),
|
|
732
|
+
"workout-alternates": mergeFlagGroups(
|
|
733
|
+
SHARED_FLAGS.help,
|
|
734
|
+
SHARED_FLAGS.output,
|
|
735
|
+
SHARED_FLAGS.jsonAndJsonl,
|
|
736
|
+
SHARED_FLAGS.session,
|
|
737
|
+
SHARED_FLAGS.credentials,
|
|
738
|
+
["id", "category"],
|
|
739
|
+
),
|
|
740
|
+
"move-workout": mergeFlagGroups(
|
|
741
|
+
SHARED_FLAGS.help,
|
|
742
|
+
SHARED_FLAGS.output,
|
|
743
|
+
SHARED_FLAGS.jsonAndJsonl,
|
|
744
|
+
SHARED_FLAGS.session,
|
|
745
|
+
SHARED_FLAGS.credentials,
|
|
746
|
+
SHARED_FLAGS.writeSafety,
|
|
747
|
+
["id", "to"],
|
|
748
|
+
),
|
|
749
|
+
"replace-workout": mergeFlagGroups(
|
|
750
|
+
SHARED_FLAGS.help,
|
|
751
|
+
SHARED_FLAGS.output,
|
|
752
|
+
SHARED_FLAGS.jsonAndJsonl,
|
|
753
|
+
SHARED_FLAGS.session,
|
|
754
|
+
SHARED_FLAGS.credentials,
|
|
755
|
+
SHARED_FLAGS.writeSafety,
|
|
756
|
+
["id", "alternate-id", "update-duration"],
|
|
757
|
+
),
|
|
758
|
+
"switch-workout": mergeFlagGroups(
|
|
759
|
+
SHARED_FLAGS.help,
|
|
760
|
+
SHARED_FLAGS.output,
|
|
761
|
+
SHARED_FLAGS.jsonAndJsonl,
|
|
762
|
+
SHARED_FLAGS.session,
|
|
763
|
+
SHARED_FLAGS.credentials,
|
|
764
|
+
SHARED_FLAGS.writeSafety,
|
|
765
|
+
["id", "mode"],
|
|
766
|
+
),
|
|
327
767
|
logout: mergeFlagGroups(SHARED_FLAGS.help, SHARED_FLAGS.output, SHARED_FLAGS.session),
|
|
328
768
|
};
|