@drakulavich/oura-cli 0.1.1 → 0.1.2
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 +13 -0
- package/README.md +96 -46
- package/dist/index.js +100 -17
- package/docs/schemas/describe.json +11 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,18 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.1.2] - 2026-05-13
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- `oura-cli describe` now reports the real CLI surface. Data commands
|
|
13
|
+
(sleep, readiness, activity, hr, spo2, stress, workout) advertise their
|
|
14
|
+
`today | date <day> | week` subcommands instead of fictional `--start/--end`
|
|
15
|
+
flags. `db` and `report` likewise list their real subcommands.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- `ManifestCommand.subcommands` field in the describe manifest, with a sibling
|
|
19
|
+
`$def` in `docs/schemas/describe.json`.
|
|
20
|
+
|
|
9
21
|
## [0.1.1] - 2026-05-13
|
|
10
22
|
|
|
11
23
|
### Added
|
|
@@ -30,5 +42,6 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
30
42
|
- Local SQLite cache at `~/.oura-cli/oura.db`.
|
|
31
43
|
- Auth via `oura-cli login`, `OURA_TOKEN`, `OURA_TOKEN_PATH`, or `~/.oura-token`.
|
|
32
44
|
|
|
45
|
+
[0.1.2]: https://github.com/drakulavich/oura-cli/releases/tag/v0.1.2
|
|
33
46
|
[0.1.1]: https://github.com/drakulavich/oura-cli/releases/tag/v0.1.1
|
|
34
47
|
[0.1.0]: https://github.com/drakulavich/oura-cli/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -1,54 +1,110 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center">oura-cli</h1>
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://github.com/drakulavich/oura-cli/actions/workflows/ci.yml"><img src="https://github.com/drakulavich/oura-cli/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
5
|
+
<a href="https://www.npmjs.com/package/@drakulavich/oura-cli"><img src="https://img.shields.io/npm/v/@drakulavich/oura-cli" alt="npm version"></a>
|
|
6
|
+
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a>
|
|
7
|
+
<a href="https://bun.sh"><img src="https://img.shields.io/badge/runtime-Bun-f9f1e1?logo=bun" alt="Bun"></a>
|
|
8
|
+
</p>
|
|
5
9
|
|
|
6
|
-
|
|
10
|
+
<p align="center"><b>Own your Oura Ring data.</b> Local CLI that pulls your biometrics from the Oura Cloud API, caches them in SQLite, and serves both your terminal and your AI agent from the same binary.</p>
|
|
7
11
|
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
+
- **Two audiences, one binary** — pretty tables when you're at a terminal, stable JSON when stdout is piped to a parent process
|
|
13
|
+
- **Self-describing** — `oura-cli describe` emits a JSON manifest of every command, argument, and output schema. Agents discover capabilities without scraping `--help`
|
|
14
|
+
- **Local-first** — everything lives in `~/.oura-cli/oura.db` after `oura-cli sync`. Query and report offline, no Oura mobile app required
|
|
15
|
+
- **Documented contract** — JSON Schemas under `docs/schemas/`, semver-versioned, plus machine-readable errors and exit codes (0–4) for clean error handling in scripts and agents
|
|
12
16
|
|
|
13
|
-
##
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
Runtime: **[Bun](https://bun.sh)** >= 1.0.
|
|
14
20
|
|
|
15
21
|
```bash
|
|
16
|
-
|
|
22
|
+
curl -fsSL https://bun.sh/install | bash # skip if Bun is already installed
|
|
23
|
+
|
|
24
|
+
bun add -g @drakulavich/oura-cli
|
|
25
|
+
oura-cli login # paste your Personal Access Token (one-time)
|
|
26
|
+
oura-cli sync # backfill recent days into ~/.oura-cli/oura.db
|
|
27
|
+
oura-cli report weekly # weekly summary with trends and recommendations
|
|
17
28
|
```
|
|
18
29
|
|
|
19
|
-
|
|
30
|
+
Get a Personal Access Token at <https://cloud.ouraring.com/personal-access-tokens>.
|
|
20
31
|
|
|
21
32
|
## For humans
|
|
22
33
|
|
|
34
|
+
Output format auto-detects: tables in your terminal, JSON when piped.
|
|
35
|
+
|
|
23
36
|
```bash
|
|
24
|
-
oura-cli
|
|
25
|
-
oura-cli
|
|
26
|
-
oura-cli
|
|
37
|
+
oura-cli sync # pull the latest from Oura Cloud
|
|
38
|
+
oura-cli db today # today's summary
|
|
39
|
+
oura-cli db date 2026-05-10 # any specific day
|
|
40
|
+
oura-cli db week # last 7 days
|
|
41
|
+
oura-cli db trends 30 # score trends across last 30 days
|
|
42
|
+
oura-cli db stats # row counts, date range, personal bests
|
|
43
|
+
oura-cli report weekly # narrative weekly summary
|
|
27
44
|
```
|
|
28
45
|
|
|
29
|
-
|
|
46
|
+
Pipe a result to your favourite JSON tool — `--format` is auto-detected, no flag needed:
|
|
30
47
|
|
|
31
|
-
|
|
48
|
+
```bash
|
|
49
|
+
oura-cli sleep week | jq '.[] | {day, score, hrv: .contributors.hrv_balance}'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Per-endpoint fetches mirror Oura's V2 API one-to-one and share the same subcommand shape:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
oura-cli sleep today # daily_sleep, today
|
|
56
|
+
oura-cli readiness date 2026-05-10 # daily_readiness, specific day
|
|
57
|
+
oura-cli activity week # daily_activity, last 7 days
|
|
58
|
+
oura-cli hr week # heartrate samples
|
|
59
|
+
oura-cli spo2 week # daily_spo2
|
|
60
|
+
oura-cli stress week # daily_stress
|
|
61
|
+
oura-cli workout week # workouts
|
|
62
|
+
```
|
|
32
63
|
|
|
33
64
|
## For agents
|
|
34
65
|
|
|
66
|
+
Designed for child-process invocation by LLM harnesses (Claude Code, Codex, generic MCP wrappers).
|
|
67
|
+
|
|
35
68
|
```bash
|
|
36
|
-
export OURA_TOKEN="…"
|
|
37
|
-
oura-cli describe
|
|
38
|
-
oura-cli sleep
|
|
69
|
+
export OURA_TOKEN="…" # no file or interactive flow needed
|
|
70
|
+
oura-cli describe # JSON manifest of commands, args, schemas
|
|
71
|
+
oura-cli sleep today # JSON (stdout is non-TTY for child processes)
|
|
72
|
+
oura-cli healthcheck # JSON: {ok, version, latencyMs}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Stable JSON I/O contract.** Output shapes are versioned with the package; breaking changes are major semver bumps. Schemas live in `docs/schemas/`.
|
|
76
|
+
|
|
77
|
+
**Machine-readable errors.** When format resolves to `json`, every error emits a single line to stderr:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{"error":{"code":"TOKEN_MISSING","message":"…","hint":"Run `oura-cli login` or set OURA_TOKEN."}}
|
|
39
81
|
```
|
|
40
82
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
83
|
+
**Exit codes:**
|
|
84
|
+
|
|
85
|
+
| Code | Meaning |
|
|
86
|
+
|------|---------|
|
|
87
|
+
| 0 | success |
|
|
88
|
+
| 1 | user error (bad arguments) |
|
|
89
|
+
| 2 | auth error (missing or invalid token) |
|
|
90
|
+
| 3 | API or network error |
|
|
91
|
+
| 4 | database or local storage error |
|
|
44
92
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
93
|
+
## What's Inside
|
|
94
|
+
|
|
95
|
+
| Endpoint | Source | Cached table |
|
|
96
|
+
|------------|-------------------------------------|-----------------------|
|
|
97
|
+
| Sleep | Oura V2 `daily_sleep` | `daily_sleep` |
|
|
98
|
+
| Readiness | Oura V2 `daily_readiness` | `daily_readiness` |
|
|
99
|
+
| Activity | Oura V2 `daily_activity` | `daily_activity` |
|
|
100
|
+
| Heart rate | Oura V2 `heartrate` | `heartrate` |
|
|
101
|
+
| SpO₂ | Oura V2 `daily_spo2` | `daily_spo2` |
|
|
102
|
+
| Stress | Oura V2 `daily_stress` | `daily_stress` |
|
|
103
|
+
| Workouts | Oura V2 `workout` | `workouts` |
|
|
104
|
+
| Sleep model | Oura V2 `sleep` | `sleep_model` |
|
|
105
|
+
| Cardiovascular age | Oura V2 `cardiovascular_age` | `cardiovascular_age` |
|
|
106
|
+
|
|
107
|
+
Runtime: [Bun](https://bun.sh). Storage: built-in `bun:sqlite`. CLI parsing: [Commander](https://github.com/tj/commander.js). Output styling: [chalk](https://github.com/chalk/chalk). Zero native dependencies, single 142 kB `dist/index.js`.
|
|
52
108
|
|
|
53
109
|
## Configuration
|
|
54
110
|
|
|
@@ -60,27 +116,21 @@ oura-cli sleep --start 2026-05-01 # JSON (since stdout is non-TTY for child pro
|
|
|
60
116
|
| Timezone | `--tz` | `OURA_TZ` | system timezone, else `UTC` |
|
|
61
117
|
| Output format | `--format` | | auto-detect (TTY → table) |
|
|
62
118
|
|
|
63
|
-
##
|
|
119
|
+
## Integrations
|
|
64
120
|
|
|
65
|
-
|
|
121
|
+
- **OpenClaw** — drop into your LLM agent as an [OpenClaw skill](https://github.com/openclaw/openclaw). `oura-cli manifest` and `oura-cli healthcheck` report back in the tool-registry shape, so the agent can discover the binary and audit its DB health automatically.
|
|
122
|
+
- **MCP** — `oura-cli describe` returns enough metadata to autogenerate an MCP server wrapper. A first-party `oura-mcp` companion is on the roadmap.
|
|
66
123
|
|
|
67
|
-
|
|
68
|
-
- `describe` — emit JSON manifest (for agents)
|
|
69
|
-
- `sleep | readiness | activity | hr | spo2 | stress | workout [--start YYYY-MM-DD] [--end YYYY-MM-DD]` — fetch from Oura API
|
|
70
|
-
- `sync` — sync all collections into the local SQLite cache
|
|
71
|
-
- `db <subcommand>` — query the local cache
|
|
72
|
-
- `report --week | --month` — render a summary
|
|
124
|
+
## Requirements
|
|
73
125
|
|
|
74
|
-
|
|
126
|
+
- [Bun](https://bun.sh) >= 1.0
|
|
127
|
+
- macOS, Linux, or Windows (WSL)
|
|
128
|
+
- An [Oura Personal Access Token](https://cloud.ouraring.com/personal-access-tokens)
|
|
75
129
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
bun install
|
|
80
|
-
bun test
|
|
81
|
-
bun run dev describe # run from source
|
|
82
|
-
```
|
|
130
|
+
## Contributing
|
|
131
|
+
|
|
132
|
+
Bug reports and pull requests welcome at [drakulavich/oura-cli/issues](https://github.com/drakulavich/oura-cli/issues).
|
|
83
133
|
|
|
84
134
|
## License
|
|
85
135
|
|
|
86
|
-
|
|
136
|
+
Made with 💍🤖 under MIT License.
|
package/dist/index.js
CHANGED
|
@@ -3683,10 +3683,6 @@ function loginCommand() {
|
|
|
3683
3683
|
}
|
|
3684
3684
|
|
|
3685
3685
|
// src/commands/describe.ts
|
|
3686
|
-
var DATE_ARGS = [
|
|
3687
|
-
{ name: "--start", type: "date", format: "YYYY-MM-DD", required: false, description: "Range start (inclusive)" },
|
|
3688
|
-
{ name: "--end", type: "date", format: "YYYY-MM-DD", required: false, description: "Range end (inclusive)" }
|
|
3689
|
-
];
|
|
3690
3686
|
function buildManifest(version) {
|
|
3691
3687
|
return {
|
|
3692
3688
|
name: "oura-cli",
|
|
@@ -3716,19 +3712,106 @@ function buildManifest(version) {
|
|
|
3716
3712
|
{ name: "--path", type: "string", required: false, description: "Override token file path" }
|
|
3717
3713
|
] },
|
|
3718
3714
|
{ name: "describe", description: "Emit a machine-readable manifest of commands, args, and outputs.", args: [] },
|
|
3719
|
-
{
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3715
|
+
{
|
|
3716
|
+
name: "sleep",
|
|
3717
|
+
description: "Fetch daily sleep scores from Oura API. Pick a subcommand: today | date <day> | week.",
|
|
3718
|
+
args: [],
|
|
3719
|
+
outputSchema: "docs/schemas/sleep.json",
|
|
3720
|
+
subcommands: [
|
|
3721
|
+
{ name: "today", description: "Today's sleep data.", args: [] },
|
|
3722
|
+
{ name: "date", description: "Sleep data for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
|
|
3723
|
+
{ name: "week", description: "Last 7 days of sleep data.", args: [] }
|
|
3724
|
+
]
|
|
3725
|
+
},
|
|
3726
|
+
{
|
|
3727
|
+
name: "readiness",
|
|
3728
|
+
description: "Fetch daily readiness scores from Oura API. Pick a subcommand: today | date <day> | week.",
|
|
3729
|
+
args: [],
|
|
3730
|
+
outputSchema: "docs/schemas/readiness.json",
|
|
3731
|
+
subcommands: [
|
|
3732
|
+
{ name: "today", description: "Today's readiness data.", args: [] },
|
|
3733
|
+
{ name: "date", description: "Readiness data for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
|
|
3734
|
+
{ name: "week", description: "Last 7 days of readiness data.", args: [] }
|
|
3735
|
+
]
|
|
3736
|
+
},
|
|
3737
|
+
{
|
|
3738
|
+
name: "activity",
|
|
3739
|
+
description: "Fetch daily activity scores from Oura API. Pick a subcommand: today | date <day> | week.",
|
|
3740
|
+
args: [],
|
|
3741
|
+
outputSchema: "docs/schemas/activity.json",
|
|
3742
|
+
subcommands: [
|
|
3743
|
+
{ name: "today", description: "Today's activity data.", args: [] },
|
|
3744
|
+
{ name: "date", description: "Activity data for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
|
|
3745
|
+
{ name: "week", description: "Last 7 days of activity data.", args: [] }
|
|
3746
|
+
]
|
|
3747
|
+
},
|
|
3748
|
+
{
|
|
3749
|
+
name: "hr",
|
|
3750
|
+
description: "Fetch heart rate samples from Oura API. Pick a subcommand: today | date <day> | week.",
|
|
3751
|
+
args: [],
|
|
3752
|
+
outputSchema: "docs/schemas/hr.json",
|
|
3753
|
+
subcommands: [
|
|
3754
|
+
{ name: "today", description: "Today's heart rate data.", args: [] },
|
|
3755
|
+
{ name: "date", description: "Heart rate data for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
|
|
3756
|
+
{ name: "week", description: "Last 7 days of heart rate data.", args: [] }
|
|
3757
|
+
]
|
|
3758
|
+
},
|
|
3759
|
+
{
|
|
3760
|
+
name: "spo2",
|
|
3761
|
+
description: "Fetch blood oxygen (SpO2) data from Oura API. Pick a subcommand: today | date <day> | week.",
|
|
3762
|
+
args: [],
|
|
3763
|
+
outputSchema: "docs/schemas/spo2.json",
|
|
3764
|
+
subcommands: [
|
|
3765
|
+
{ name: "today", description: "Today's SpO2 data.", args: [] },
|
|
3766
|
+
{ name: "date", description: "SpO2 data for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
|
|
3767
|
+
{ name: "week", description: "Last 7 days of SpO2 data.", args: [] }
|
|
3768
|
+
]
|
|
3769
|
+
},
|
|
3770
|
+
{
|
|
3771
|
+
name: "stress",
|
|
3772
|
+
description: "Fetch daily stress data from Oura API. Pick a subcommand: today | date <day> | week.",
|
|
3773
|
+
args: [],
|
|
3774
|
+
outputSchema: "docs/schemas/stress.json",
|
|
3775
|
+
subcommands: [
|
|
3776
|
+
{ name: "today", description: "Today's stress data.", args: [] },
|
|
3777
|
+
{ name: "date", description: "Stress data for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
|
|
3778
|
+
{ name: "week", description: "Last 7 days of stress data.", args: [] }
|
|
3779
|
+
]
|
|
3780
|
+
},
|
|
3781
|
+
{
|
|
3782
|
+
name: "workout",
|
|
3783
|
+
description: "Fetch workout data from Oura API. Pick a subcommand: today | date <day> | week.",
|
|
3784
|
+
args: [],
|
|
3785
|
+
outputSchema: "docs/schemas/workout.json",
|
|
3786
|
+
subcommands: [
|
|
3787
|
+
{ name: "today", description: "Today's workout data.", args: [] },
|
|
3788
|
+
{ name: "date", description: "Workout data for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
|
|
3789
|
+
{ name: "week", description: "Last 7 days of workout data.", args: [] }
|
|
3790
|
+
]
|
|
3791
|
+
},
|
|
3726
3792
|
{ name: "sync", description: "Sync all Oura collections into the local database.", args: [] },
|
|
3727
|
-
{
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3793
|
+
{
|
|
3794
|
+
name: "db",
|
|
3795
|
+
description: "Query and manage the local SQLite cache. Pick a subcommand.",
|
|
3796
|
+
args: [],
|
|
3797
|
+
subcommands: [
|
|
3798
|
+
{ name: "today", description: "Today's summary from local DB.", args: [] },
|
|
3799
|
+
{ name: "date", description: "Summary for a specific date.", args: [{ name: "<day>", type: "date", format: "YYYY-MM-DD", required: true, description: "Target date." }] },
|
|
3800
|
+
{ name: "week", description: "Last 7 days from local DB.", args: [] },
|
|
3801
|
+
{ name: "trends", description: "Score and metric trends over N days (default 30).", args: [{ name: "[days]", type: "number", required: false, description: "Window size in days." }] },
|
|
3802
|
+
{ name: "stats", description: "Row counts, date range, record highs.", args: [] },
|
|
3803
|
+
{ name: "import", description: "Sync new data from Oura API into the local DB.", args: [] },
|
|
3804
|
+
{ name: "reset", description: "Destroy and rebuild the database from exported CSVs.", args: [] }
|
|
3805
|
+
]
|
|
3806
|
+
},
|
|
3807
|
+
{
|
|
3808
|
+
name: "report",
|
|
3809
|
+
description: "Render a narrative health report. Pick a subcommand.",
|
|
3810
|
+
args: [],
|
|
3811
|
+
subcommands: [
|
|
3812
|
+
{ name: "weekly", description: "Weekly health summary with trends and recommendations.", args: [] }
|
|
3813
|
+
]
|
|
3814
|
+
}
|
|
3732
3815
|
]
|
|
3733
3816
|
};
|
|
3734
3817
|
}
|
|
@@ -3739,7 +3822,7 @@ function describeCommand(version) {
|
|
|
3739
3822
|
}
|
|
3740
3823
|
|
|
3741
3824
|
// src/index.ts
|
|
3742
|
-
var VERSION = "0.1.
|
|
3825
|
+
var VERSION = "0.1.2";
|
|
3743
3826
|
var program2 = new Command;
|
|
3744
3827
|
program2.name("oura-cli").description("Oura Ring CLI \u2014 query and analyze Oura Ring health data. Designed for humans and agents.").version(VERSION).option("--format <format>", "Output format: table | json (default auto-detect by TTY)").option("--token <pat>", "Inline access token (prefer env vars or `oura-cli login`)").option("--db <path>", "Path to SQLite database file (env: OURA_DB_PATH)").option("--tz <timezone>", "Display timezone (env: OURA_TZ; default auto-detect)");
|
|
3745
3828
|
program2.addCommand(loginCommand());
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"name": { "type": "string" },
|
|
38
38
|
"description": { "type": "string" },
|
|
39
39
|
"args": { "type": "array", "items": { "$ref": "#/$defs/arg" } },
|
|
40
|
-
"outputSchema": { "type": "string" }
|
|
40
|
+
"outputSchema": { "type": "string" },
|
|
41
|
+
"subcommands": { "type": "array", "items": { "$ref": "#/$defs/subcommand" } }
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
}
|
|
@@ -54,6 +55,15 @@
|
|
|
54
55
|
"description": { "type": "string" },
|
|
55
56
|
"values": { "type": "array", "items": { "type": "string" } }
|
|
56
57
|
}
|
|
58
|
+
},
|
|
59
|
+
"subcommand": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"required": ["name", "description", "args"],
|
|
62
|
+
"properties": {
|
|
63
|
+
"name": { "type": "string" },
|
|
64
|
+
"description": { "type": "string" },
|
|
65
|
+
"args": { "type": "array", "items": { "$ref": "#/$defs/arg" } }
|
|
66
|
+
}
|
|
57
67
|
}
|
|
58
68
|
}
|
|
59
69
|
}
|
package/package.json
CHANGED