Skip to main content

Garmin

@svendowideit/garminv2026.10.12.1· 11d agoBEHAVIORSWORKFLOWS
01README

Sync a Garmin Connect account into swamp: one authenticated transport, plus domain models that read its cached responses — devices with a capability map that gates the rest, the activity history with FIT/TCX/GPX downloads, daily wellness, weight/body composition, and training + performance metrics.

Garmin Connect has no public API. It needs a signed OAuth1 handshake to mint a rotating OAuth2 bearer token, it rate-limits aggressively, and every data domain would otherwise repeat that fragile login. This extension does it once: @svendowideit/garmin-connect owns login, token refresh, authenticated cached GETs and binary downloads, while domain models declare the connectapi paths they need and parse the cached bodies. The seam is a workflow: transport fetch → domain sync. Because Garmin features depend on the devices an account owns, the devices model derives a capability map so later workflows can gate on it instead of failing on data a device cannot produce; and the wellness and performance models drop metrics (HRV, SpO2, VO2max, FTP) whose devices are absent.

WHAT IT DOES

It turns a fragile, undocumented fitness API into composable swamp data. One model owns the whole auth chain (SSO → OAuth1 → OAuth2), a refresh token lasts weeks so unattended runs keep working with no password, responses are cached on disk so a rate-limited or offline run degrades to last-known data, and every domain fetch is one paced batch. You get a normalised activity history with per-activity splits and weather plus original activity files as first-class swamp files; a per-day wellness roll-up (steps, sleep and sleep score, stress, heart rate, body battery); weight with body composition; and training + performance metrics (training status and readiness, VO2max per sport, race predictions, endurance and hill score, FTP, personal records), all gated by a device capability map.

INSTALL

swamp extension pull @svendowideit/garmin

DEPENDENCIES

None. It uses only Deno's built-in fetch and WebCrypto (the OAuth1 HMAC-SHA1 signer is implemented here), plus the bundled deno binary swamp already ships. Nothing is installed on the host.

RUN

# See what is configured and the exact commands to fix any gap (read-only).
swamp model @svendowideit/garmin-connect method run setup garmin-connect

# Preferred: seed the session from a base64 garth token store, so no
# password is ever handled and scheduled runs need no interactivity.
swamp vault put garmin-secrets GARMIN_TOKEN_STORE
swamp model @svendowideit/garmin-connect method run import-tokens garmin-connect

# Or log in with credentials; add --input interactiveMfa=<code> if Garmin
# asks for a one-time code.
swamp vault put garmin-secrets GARMIN_EMAIL
swamp vault put garmin-secrets GARMIN_PASSWORD
swamp model @svendowideit/garmin-connect method run login garmin-connect

# Refresh the session then sync each domain; these run in sequence on
# their own schedule once 'swamp serve' is up.
swamp workflow run @svendowideit/garmin-session
swamp workflow run @svendowideit/garmin-devices-sync
swamp workflow run @svendowideit/garmin-activities-sync
swamp workflow run @svendowideit/garmin-health-sync
swamp workflow run @svendowideit/garmin-body-sync
swamp workflow run @svendowideit/garmin-performance-sync

# Backfill a wider window (any domain sync accepts a date range).
swamp workflow run @svendowideit/garmin-performance-sync --input startDate=2026-01-01 --input endDate=2026-01-31

# Download original activity files (FIT by default) for the synced list.
swamp workflow run @svendowideit/garmin-download --input format=tcx --input limit=50

# Read the data: wellness, weight, or performance and records.
swamp data get garmin-health health-range --json \
  | jq '.content.summaries[] | {date, steps, sleepScore, avgStress}'
swamp data get garmin-performance records --json \
  | jq '.content | {ftp, best, personalRecords}'
swamp data get garmin-performance metrics-<date> --json \
  | jq '.content | {trainingStatus, trainingReadiness, vo2maxRunning}'

CONFIGURE

All optional, set on the model instance (recommended — scheduled runs have no --input) or overridden per call with --input:

domain               Region domain (default garmin.com; garmin.cn for China)
vaultName            Vault for credentials + session (default garmin-secrets). The session and pending-MFA token resources are pinned to garmin-secrets, so sensitive tokens are always written there regardless of other vaults.
cacheDir             Shared response cache (default ~/.swamp/garmin-cache)
requestDelayMs       Minimum delay between origin requests (default 500)
maxRetries           Retries after 429/5xx (default 2)
maxFetchesPerCall    Origin-fetch cap per fetch-many/download-many (default 100)
capabilityOverrides  Force capabilities true/false when detection misses
detailKinds          Per-activity detail to fetch (default detail, splits, weather)
metrics              Wellness and performance metrics to fetch (see README)
respectCapabilities  Drop device-gated metrics (default true)
timezone             IANA zone for activity bucketing and the wellness
                     and body default window; empty = host local. Set it
                     so a morning run fetches the day you are living in.
days                 Local wellness/body days through today (default 3; cap 14)
unit                 Body display unit: kg or lb (default kg; grams always kept)

See every setting and its current value: swamp model @svendowideit/garmin-connect method run setup garmin-connect swamp model @svendowideit/garmin-devices method run setup garmin-devices swamp model @svendowideit/garmin-activities method run setup garmin-activities swamp model @svendowideit/garmin-health method run setup garmin-health swamp model @svendowideit/garmin-body method run setup garmin-body swamp model @svendowideit/garmin-performance method run setup garmin-performance

SETUP

Every model ships a read-only setup method: run it to see the config that was detected or reused and exactly what is missing before a sync runs (it prints no secret). Global arguments are persisted on the model and read back on every run, credentials and session live in the vault, and each model verifies the real state — a valid session, cached responses, a resolvable window — stopping with a clear message on a blocking failure. Capabilities enable progressively: a model fetches only the domains its config and the account's device capability map support.

UPGRADE

Nothing is installed on the host, so there is no in-place rebuild to run. Upgrade by updating the extension; each model carries its own upgrades entry and migrates its stored configuration on the next run, reusing the existing vault session and cache.

swamp extension update @svendowideit/garmin

WHAT IT INSTALLS

Nothing on the host — no services, daemons, or webhooks. Six cron triggers are registered while 'swamp serve' runs, staggered through the morning: garmin-session at 0 5 * * *, garmin-devices-sync at 10 5 * * *, garmin-activities-sync at 20 5 * * *, garmin-health-sync at 30 5 * * *, garmin-body-sync at 40 5 * * *, and garmin-performance-sync at 50 5 * * *. Downloads run on demand. All state lives in swamp resources; the session tokens live in the vault (marked sensitive), pinned to the garmin-secrets vault (vaultName on the session and pendingMfa resource specs) so creating another vault can never redirect them — see the README's 'Vault pinning'.

02Behaviors6
@svendowideit/garmin-connectv2026.10.01.1garmin_connect.ts

Global Arguments

ArgumentTypeDescription
domain?stringGarmin region domain; use garmin.cn for the China region
vaultName?stringVault the model reads credentials from and persists the session into
usernameKey?stringVault key holding the Garmin account email
passwordKey?stringVault key holding the Garmin account password
mfaCodeKey?stringVault key holding a one-time MFA code for a non-interactive first login
tokenStoreKey?stringVault key holding a base64 `garth` token store (preferred: no password)
username?stringGarmin email; normally left empty and read from the vault
password?stringGarmin password; normally left empty and read from the vault
tokenStore?stringInline base64 token store; normally left empty and read from the vault
cacheDir?stringShared directory raw Garmin responses are cached under
requestDelayMs?numberMinimum delay between origin requests, across runs (0 disables pacing)
maxRetries?numberRetries after HTTP 429/5xx, with exponential backoff
retryDelayMs?numberBase backoff after a retryable failure (Retry-After takes precedence)
requestTimeoutMs?numberWall-clock budget for a single Garmin request
maxFetchesPerCall?numberMaximum origin fetches a single fetch-many call makes (cached hits are free)
defaultMaxAgeMs?numberDefault freshness window for cached responses (0 = always prefer the cache)
fn setup()
Report Garmin configuration readiness (region, vault keys, session
fn import-tokens(tokenStore?: string)
Seed the session from a base64 `garth` token store (Client.dumps
ArgumentTypeDescription
tokenStore?stringBase64 `garth` token store; omit to read it from the vault
fn ensure()
Ensure a usable Garmin session: reuse the stored bearer token when
fn profile(displayName?: string)
Fetch the account's social profile and write the `profile` resource.
ArgumentTypeDescription
displayName?stringSet the display name directly, skipping the network call
fn login(interactiveMfa?: string)
Run the Garmin SSO → OAuth1 → OAuth2 login and persist the session.
ArgumentTypeDescription
interactiveMfa?stringOne-time MFA code to complete a challenge raised on a previous login run
fn fetch()
Authenticated GET of one connectapi path, cached on disk under
fn fetch-many(paths: array, maxFetches?: number)
Authenticated GET of many connectapi paths in one call, each cached
ArgumentTypeDescription
pathsarrayconnectapi paths to fetch (each cached independently; blanks skipped)
maxFetches?numberCap on origin fetches this call (omit = global maxFetchesPerCall)
fn download(activityId: string, format?: enum)
Download one activity export (fit/tcx/gpx/kml/csv) and store it as a
ArgumentTypeDescription
activityIdstringGarmin activity id (numeric string)
format?enumExport format; fit returns Garmin's original ZIP-wrapped FIT file
fn download-many(activityIds: array, format?: enum, maxDownloads?: number, skipExisting?: boolean)
Download many activity exports in one call, storing each as a file
ArgumentTypeDescription
activityIdsarrayActivity ids to download. Already-downloaded ids are skipped, so a
format?enumExport format applied to every id
maxDownloads?numberCap on downloads this call (omit = global maxFetchesPerCall). Ids beyond
skipExisting?booleanSkip ids whose export is already stored, so re-runs are cheap

Resources

session(infinite)— Authenticated Garmin session; tokens are sensitive and stored in the vault
fetch(infinite)— One cached connectapi response (metadata + raw body)
batch(infinite)— Summary of a fetch-many call
download(infinite)— Metadata for a downloaded binary activity export
downloads(infinite)— Summary of a download-many call
profile(infinite)— The account's social profile, including the display name several
setup(infinite)— Configuration-readiness report
status(infinite)— Session readiness written by `ensure`, so a workflow can guard on
pendingMfa(infinite)— A login suspended on MFA: the SSO cookie jar needed to call

Files

export(application/octet-stream)— A downloaded activity export (FIT/TCX/GPX/KML/CSV)
@svendowideit/garmin-devicesv2026.10.04.1garmin_devices.ts

Global Arguments

ArgumentTypeDescription
cacheDir?stringShared cache directory to read the transport's responses from (must
capabilityOverrides?recordForce specific capabilities true/false, overriding detection. Use this
fn setup()
Report whether the device/settings responses are cached and what
fn paths()
Return the connectapi paths this model needs. Feed the result to
fn sync(overrides?: record)
Parse the cached device inventory and user settings, write the
ArgumentTypeDescription
overrides?recordPer-run capability overrides, merged over the global ones

Resources

devices(infinite)— Normalised registered device inventory
capabilities(infinite)— Derived capability map that gates other Garmin domain models. Guard
paths(infinite)— The connectapi paths this model needs the transport to fetch
setup(infinite)— Configuration-readiness report
@svendowideit/garmin-activitiesv2026.10.04.1garmin_activities.ts

Global Arguments

ArgumentTypeDescription
cacheDir?stringShared cache directory to read the transport's responses from (must
detailKinds?arrayWhich per-activity sub-resources `detail-paths` requests by default
timezone?stringIANA timezone (e.g. Australia/Brisbane) used to bucket activity start
fn setup()
Report the configured window, detail kinds, and whether the activity
fn activity-list-path(days?: number, startDate?: string, endDate?: string, timezone?: string, activityType?: string, limit?: number, sortOrder?: enum)
Build the date-ranged activity list path and write it as `paths`. A
ArgumentTypeDescription
days?numberHow many days back from today to list (ignored when startDate is set)
startDate?stringExplicit start date, YYYY-MM-DD (overrides `days`)
endDate?stringExplicit end date, YYYY-MM-DD (defaults to today)
timezone?stringOverride the global timezone for this call when resolving the default
activityType?stringFilter by Garmin activity type key (e.g. cycling, running, swimming)
limit?numberMaximum activities to request in one page
sortOrder?enumNewest-first (desc) or oldest-first (asc)
fn detail-paths(ids?: array, kinds?: array, useLastList?: boolean)
Build per-activity detail paths for the transport's single fetch-many
ArgumentTypeDescription
ids?arrayActivity ids whose detail paths to build
kinds?arrayOverride the global detailKinds for this call
useLastList?booleanWhen `ids` is omitted, read the ids from this model's most recent
fn sync(ids?: array, includeDetails?: boolean)
Parse the cached activity list into `activity-list` plus one
ArgumentTypeDescription
ids?arrayLimit parsing to these activity ids. Omit to parse the latest list.
includeDetails?booleanAlso parse any cached per-activity detail responses into `activity-detail`

Resources

activity(infinite)— One normalised activity, keyed by Garmin activity id
list(infinite)— All activities in the window in one resource, so a workflow can pass
detail(infinite)— A per-activity detail response (splits, weather, HR zones, …), keyed
paths(infinite)— The connectapi paths the transport should fetch
setup(infinite)— Configuration-readiness report
@svendowideit/garmin-healthv2026.10.04.1garmin_health.ts

Global Arguments

ArgumentTypeDescription
cacheDir?stringShared cache directory to read the transport's responses from (must
metrics?array
respectCapabilities?booleanDrop device-gated metrics the account's devices do not support, using
timezone?stringIANA timezone (e.g. Australia/Brisbane) used to resolve the default
days?numberNumber of local days to fetch (through today) when no explicit date or
fn setup()
Report the configured metrics, capability gating, and whether the
fn paths(date?: string, startDate?: string, endDate?: string, metrics?: array, timezone?: string, days?: number, capabilities?: record, displayName?: string, maxDays?: number)
Build the connectapi paths for the requested dates and metrics. Reads
ArgumentTypeDescription
date?stringSingle day to fetch, YYYY-MM-DD (defaults to yesterday — a full day's data)
startDate?stringRange start, YYYY-MM-DD (overrides `date`)
endDate?stringRange end, YYYY-MM-DD (defaults to startDate)
metrics?arrayOverride the global metrics for this call
timezone?stringOverride the global timezone for resolving the default date window.
days?numberOverride the global `days` (local days through today) for the default
capabilities?recordThe device capability map, wired in by the workflow from
displayName?stringAccount display name, wired in by the workflow from the transport's
maxDays?numberSafety cap on the number of days a range may expand to
fn sync(date?: string, startDate?: string, endDate?: string, metrics?: array, displayName?: string)
Parse the cached wellness bodies into one `daily-<date>` per day and a
ArgumentTypeDescription
date?stringSingle day to parse, YYYY-MM-DD
startDate?stringRange start, YYYY-MM-DD
endDate?stringRange end, YYYY-MM-DD
metrics?arrayMetrics to parse (must match the paths that were fetched)
displayName?stringAccount display name; falls back to the cached social profile

Resources

daily(infinite)— One day's wellness summary plus the raw per-metric bodies, keyed by date
range(infinite)— A multi-day window in one resource, with a compact summary per day
paths(infinite)— The connectapi paths the transport should fetch
setup(infinite)— Configuration-readiness report
@svendowideit/garmin-bodyv2026.10.09.1garmin_body.ts

Global Arguments

ArgumentTypeDescription
cacheDir?stringShared cache directory to read the transport's responses from (must
unit?enumDisplay unit for the normalised `weight` field; raw grams are always kept
timezone?stringIANA timezone (e.g. Australia/Brisbane) used to resolve the default date
days?numberNumber of local days to fetch (through today) when no explicit date or
fn setup()
Report the configured unit and whether any body-composition data has
fn paths(date?: string, startDate?: string, endDate?: string, timezone?: string, days?: number, maxDays?: number, mode?: enum)
Build the connectapi paths for the requested window. `mode=range`
ArgumentTypeDescription
date?stringSingle day, YYYY-MM-DD (overrides the range)
startDate?stringRange start, YYYY-MM-DD
endDate?stringRange end, YYYY-MM-DD
timezone?stringOverride the global timezone for resolving the default date window.
days?numberOverride the global `days` (local days through today) for the default
maxDays?numberSafety cap on range days when fanning out per-day paths
mode?enum`range` = one range request; `daily` = one day-view request per day
fn sync(date?: string, startDate?: string, endDate?: string)
Parse the cached weigh-in bodies into `weigh-in-<date>` resources and
ArgumentTypeDescription
date?stringSingle day to parse, YYYY-MM-DD
startDate?stringRange start, YYYY-MM-DD
endDate?stringRange end, YYYY-MM-DD

Resources

weighIn(infinite)— One normalised weigh-in, keyed by date
range(infinite)— All weigh-ins in the window plus whether body composition was present
paths(infinite)— The connectapi paths the transport should fetch
setup(infinite)— Configuration-readiness report
@svendowideit/garmin-performancev2026.10.01.1garmin_performance.ts

Global Arguments

ArgumentTypeDescription
cacheDir?stringShared cache directory to read the transport's responses from (must
metrics?array
respectCapabilities?booleanDrop device-gated metrics the account's devices do not support, using
fn setup()
Report the configured metrics, capability gating, and display-name
fn paths(date?: string, startDate?: string, endDate?: string, metrics?: array, capabilities?: record, displayName?: string, latestOnly?: boolean, maxDays?: number)
Build the connectapi paths for the requested dates and metrics. Daily
ArgumentTypeDescription
date?stringSingle day for daily metrics, YYYY-MM-DD (defaults to yesterday)
startDate?stringRange start, YYYY-MM-DD
endDate?stringRange end, YYYY-MM-DD
metrics?arrayOverride the global metrics for this call
capabilities?recordDevice capability map, wired in by the workflow
displayName?stringAccount display name, wired in by the workflow; falls back to the cached profile
latestOnly?booleanFetch only the latest (non-date) metrics — cheap, useful for a quick refresh
maxDays?numberSafety cap on the number of days a range may expand to
fn sync(date?: string, startDate?: string, endDate?: string, displayName?: string)
Parse the cached performance bodies into `metrics-<date>` per day, a
ArgumentTypeDescription
date?stringSingle day to parse, YYYY-MM-DD
startDate?stringRange start, YYYY-MM-DD
endDate?stringRange end, YYYY-MM-DD
displayName?stringAccount display name; falls back to the cached social profile

Resources

metrics(infinite)— One day's performance metrics plus the raw bodies, keyed by date
range(infinite)— A multi-day window in one resource with a row per day
records(infinite)— Latest values with no date (FTP, personal records) plus bests seen
paths(infinite)— The connectapi paths the transport should fetch
setup(infinite)— Configuration-readiness report
03Workflows7
@svendowideit/garmin-session9d7331e0-4842-4573-9ff6-2592f50b0e30

Ensure an authenticated Garmin Connect session before any Garmin data sync. This is the common transport workflow the other @svendowideit/garmin workflows call. It never fetches Garmin data itself — it only guarantees that @svendowideit/garmin-connect holds a usable bearer token: 1. `ensure` reuses a valid stored token, or refreshes an expired one with the stored OAuth1 token (no credentials needed), and writes a `status` resource. 2. `login` runs the SSO →

sessionGuarantee a usable Garmin bearer token.
1.ensure— Reuse or refresh the stored session; writes a `status` resource.
2.login— Run the login chain only when the session is not already ready. Missing credentials are tolerated here and surfaced by the closing assert.
3.verify— Re-run ensure so `status` reflects the outcome of login.
4.require-session— Fail with a precise fix when no usable session exists.
@svendowideit/garmin-devices-syncc5afb6b5-4acb-4587-8447-f09b61ebe059

Sync the Garmin device inventory and derive the capability map. This workflow is the reference example of the transport→domain seam the whole @svendowideit/garmin family uses: 1. session call the common @svendowideit/garmin-session workflow, which guarantees a valid bearer token (refreshing if needed). 2. device-paths ask @svendowideit/garmin-devices which connectapi paths it needs, so the paths live next to the parser, not here. 3. fetch

devicesEnsure a session, fetch device responses, derive capabilities.
1.session— Guarantee a valid Garmin bearer token before any fetch. Reusable transport workflow; refreshes an expired token with no credentials.
2.device-paths— Ask the domain model which connectapi paths it needs.
3.fetch— One authenticated, paced, cached batch fetch of the device paths.
4.sync— Parse the cached bodies into `device-list` and `device-capabilities`.
5.report-capabilities— Fail loudly if no devices were found (usually a stale token).
@svendowideit/garmin-activities-syncf45bd969-6ef9-4ada-8b85-359d153150db

Sync the Garmin activity history for a date window. Same transport→domain seam as garmin-devices-sync, scaled to a high-volume domain: 1. session guarantee a valid bearer token (transport workflow). 2. list-path @svendowideit/garmin-activities builds the date-ranged, paged activity list path for the window. 3. detail-paths turn the ids returned by the previous run's list into per-activity detail paths (splits, weather, HR z

activitiesEnsure a session, fetch the list + details, parse into resources.
1.session— Guarantee a valid Garmin bearer token before any fetch.
2.list-path— Build the date-ranged activity list path for the window.
3.detail-paths— Build per-activity detail paths from the ids the previous run synced. Omit `ids` so the model reads its own last `activity-list` — no fragile CEL here. On the first run there is no list, so the result is empty.
4.fetch-list— Fetch (and cache) the activity list in one call.
5.fetch-details— Fetch (and cache) every per-activity detail path in one batch. Guarded so it is skipped when there are no detail paths (first run, or includeDetails=false).
6.sync— Parse the cached list and detail bodies into `activity-list`, `activity-<id>` and `detail-<id>-<kind>` resources.
7.report— Fail loudly if the window produced no activities at all.
@svendowideit/garmin-downloade40db117-34fb-443a-b3a8-abbbfd9ca2c6

Download original activity files (FIT by default) for a synced date window. Sits on top of the activities sync and the transport's fan-out download: 1. session guarantee a valid bearer token. 2. activity-ids read the ids from @svendowideit/garmin-activities' `activity-list` (synced by garmin-activities-sync). 3. download-all @svendowideit/garmin-connect's download-many stores each export as a swamp file artefact in ONE call. Ids already

downloadEnsure a session and download activity exports in one batch.
1.session— Guarantee a valid Garmin bearer token before downloading.
2.require-list— Fail with a clear fix when the activity list is missing.
3.download-all— Download every listed activity's export in one fan-out call. Skips existing files and caps downloads per run.
4.report— Confirm the batch summary exists so the run is auditable.
@svendowideit/garmin-health-syncbbee58f5-d1ed-4eb9-b57c-08b08e5f8c9f

Sync daily wellness from Garmin Connect for a date window. Same transport→domain seam as the other Garmin syncs, but with a prerequisite the device endpoints do not need: 1. session guarantee a valid bearer token. 2. profile fetch the account's social profile. Several Garmin wellness endpoints are addressed by the display name, which this records so the health model can build their paths. 3. health-paths @svendowideit/garmin-health build

healthEnsure a session, fetch wellness paths, parse into resources.
1.session— Guarantee a valid Garmin bearer token before any fetch.
2.profile— Fetch the account display name — several wellness endpoints are addressed by it.
3.health-paths— Build one path per date per metric, gated by the device capability map.
4.fetch— One authenticated, paced, cached batch fetch of all paths.
5.sync— Parse the cached bodies into `daily-<date>` and a `health-range` roll-up.
6.report— Fail loudly if no day could be parsed at all.
@svendowideit/garmin-body-sync6366638a-7096-4a93-bc7e-f20475a6f298

Sync weight and body composition from Garmin Connect. The simplest transport→domain seam of the family, because the weight endpoints need no display name: 1. session guarantee a valid bearer token. 2. body-paths @svendowideit/garmin-body builds the weigh-in paths for the window — one range request by default, or one day-view request per day when a date may hold several weigh-ins. 3. fetch one fetch-many caches them. 4. sync parse into

bodyEnsure a session, fetch weigh-in paths, parse into resources.
1.session— Guarantee a valid Garmin bearer token before any fetch.
2.body-paths— Build the weigh-in paths for the window and mode.
3.fetch— One authenticated, paced, cached batch fetch of the paths.
4.sync— Parse into `weigh-in-<date>` resources and a `body-range`.
5.report— Confirm the roll-up exists so the run is auditable.
@svendowideit/garmin-performance-syncb6de792b-225e-4061-aa7a-2fcd08324701

Sync training and performance metrics from Garmin Connect. The same transport→domain seam as the health sync, with a mixed metric set: 1. session guarantee a valid bearer token. 2. profile fetch the account display name (race predictions and personal records are addressed by it). 3. perf-paths @svendowideit/garmin-performance builds the paths — one per day for date-keyed metrics, one total for the latest metrics (FTP, pers

performanceEnsure a session, fetch performance paths, parse into resources.
1.session— Guarantee a valid Garmin bearer token before any fetch.
2.profile— Fetch the account display name — race predictions and personal records are addressed by it.
3.perf-paths— Build daily and latest metric paths, gated by the device capability map.
4.fetch— One authenticated, paced, cached batch fetch of all paths.
5.sync— Parse into `metrics-<date>`, a `performance-range`, and `records`.
6.report— Confirm the roll-up exists so the run is auditable.
04Previous Versions3
2026.10.09.1
2026.10.04.1

Modified 2 behaviors

2026.10.01.1
05Stats
A
93 / 100
Downloads
28
Archive size
146.8 KB
  • Has README or module doc2/2earned
  • README has a code example1/1earned
  • README is substantive1/1earned
  • Most symbols documented1/1earned
  • No slow types (deprecated)1/1earned
  • Dependencies pass trust audit2/2earned
  • Has description1/1earned
  • Platform support declared (or universal)2/2earned
  • License1/2partial

    no SPDX license declared

  • Verified public repository2/2earned
06Platforms