@drakulavich/oura-cli 0.1.3 → 0.2.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 CHANGED
@@ -6,6 +6,29 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.0] - 2026-05-13
10
+
11
+ ### Changed
12
+ - **BREAKING (typescript only):** `ErrorCode` is now a closed union of the
13
+ documented codes (`BAD_ARGS`, `TOKEN_MISSING`, `TOKEN_INVALID`, `API_ERROR`,
14
+ `DB_ERROR`, `UNKNOWN`). External code constructing `CliError` with a custom
15
+ string code will fail to compile. Runtime behaviour for already-built code
16
+ is unchanged. (#1, item 5)
17
+ - `exitCodeFor` is now an exhaustive `switch` over the closed union; future
18
+ additions to `ErrorCode` require a corresponding branch.
19
+
20
+ ### Added
21
+ - `describe` manifest now includes `compatManifestCommand: "oura-cli manifest"`
22
+ so agents can discover the OpenClaw-compatible second manifest. (#1, item 6)
23
+ - README documents the two-manifest split under "Manifest formats".
24
+ - `getGlobalOpts(command)` helper in `src/commands/helpers.ts` walks the
25
+ commander parent chain to the root program; api-command.ts and others now
26
+ use it instead of `command.parent!.parent!.opts()`. (#1, item 7)
27
+
28
+ ### Security
29
+ - API error messages now redact `Bearer <token>` and `"token":"<value>"`
30
+ patterns and truncate bodies past 200 chars before printing. (#1, item 18)
31
+
9
32
  ## [0.1.3] - 2026-05-13
10
33
 
11
34
  ### Fixed
@@ -63,6 +86,7 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
63
86
  - Local SQLite cache at `~/.oura-cli/oura.db`.
64
87
  - Auth via `oura-cli login`, `OURA_TOKEN`, `OURA_TOKEN_PATH`, or `~/.oura-token`.
65
88
 
89
+ [0.2.0]: https://github.com/drakulavich/oura-cli/releases/tag/v0.2.0
66
90
  [0.1.3]: https://github.com/drakulavich/oura-cli/releases/tag/v0.1.3
67
91
  [0.1.2]: https://github.com/drakulavich/oura-cli/releases/tag/v0.1.2
68
92
  [0.1.1]: https://github.com/drakulavich/oura-cli/releases/tag/v0.1.1
package/README.md CHANGED
@@ -90,6 +90,22 @@ oura-cli healthcheck # JSON: {ok, version, latencyMs}
90
90
  | 3 | API or network error |
91
91
  | 4 | database or local storage error |
92
92
 
93
+ ## Manifest formats
94
+
95
+ Two manifest commands, two audiences:
96
+
97
+ - **`oura-cli describe`** — neutral, agent-friendly. Lists every command, its
98
+ args, output schema refs, and exit-code semantics. Use this when integrating
99
+ with generic LLM harnesses, MCP wrappers, or your own custom scripts.
100
+ - **`oura-cli manifest`** — [OpenClaw](https://github.com/openclaw/openclaw)
101
+ `tool-registry` shape. Strictly smaller, optimised for OpenClaw's skill
102
+ discovery and health-aggregation flow. Use this only if you're plugging
103
+ oura-cli into an OpenClaw gateway.
104
+
105
+ Both return JSON. `describe` references `manifest` via the
106
+ `compatManifestCommand` field so an agent can discover the second format
107
+ without prior knowledge.
108
+
93
109
  ## What's Inside
94
110
 
95
111
  | Endpoint | Source | Cached table |
package/dist/index.js CHANGED
@@ -2647,17 +2647,25 @@ class CliError extends Error {
2647
2647
  this.name = "CliError";
2648
2648
  }
2649
2649
  }
2650
- var EXIT_CODE_BY_CODE = {
2651
- BAD_ARGS: 1,
2652
- TOKEN_MISSING: 2,
2653
- TOKEN_INVALID: 2,
2654
- API_ERROR: 3,
2655
- DB_ERROR: 4
2656
- };
2657
2650
  function exitCodeFor(err) {
2658
- if (err instanceof CliError)
2659
- return EXIT_CODE_BY_CODE[err.code] ?? 1;
2660
- return 1;
2651
+ if (!(err instanceof CliError))
2652
+ return 1;
2653
+ switch (err.code) {
2654
+ case "BAD_ARGS":
2655
+ return 1;
2656
+ case "TOKEN_MISSING":
2657
+ case "TOKEN_INVALID":
2658
+ return 2;
2659
+ case "API_ERROR":
2660
+ return 3;
2661
+ case "DB_ERROR":
2662
+ return 4;
2663
+ case "UNKNOWN":
2664
+ return 1;
2665
+ }
2666
+ }
2667
+ function redactSecrets(s) {
2668
+ return s.replace(/Bearer\s+[A-Za-z0-9._\-]{8,}/g, "Bearer [REDACTED]").replace(/"token"\s*:\s*"[^"]{8,}"/g, '"token":"[REDACTED]"');
2661
2669
  }
2662
2670
  function formatError(err, format) {
2663
2671
  const code = err instanceof CliError ? err.code : "UNKNOWN";
@@ -2706,7 +2714,9 @@ class OuraClient {
2706
2714
  headers: { Authorization: `Bearer ${this.token}` }
2707
2715
  });
2708
2716
  if (!response.ok) {
2709
- const body = await response.text();
2717
+ const rawBody = await response.text();
2718
+ const redacted = redactSecrets(rawBody);
2719
+ const body = redacted.length > 200 ? redacted.slice(0, 200) + "\u2026 (truncated)" : redacted;
2710
2720
  if (response.status === 401 || response.status === 403) {
2711
2721
  throw new CliError("TOKEN_INVALID", `Oura API ${response.status}: ${body}`);
2712
2722
  }
@@ -2752,6 +2762,13 @@ function resolveDefaultTimezone() {
2752
2762
  }
2753
2763
 
2754
2764
  // src/commands/helpers.ts
2765
+ function getGlobalOpts(command) {
2766
+ let node = command;
2767
+ while (node?.parent) {
2768
+ node = node.parent;
2769
+ }
2770
+ return node?.opts() ?? {};
2771
+ }
2755
2772
  function getClient(opts) {
2756
2773
  return new OuraClient(opts.token ? { token: opts.token } : {});
2757
2774
  }
@@ -2770,19 +2787,19 @@ function dateRange(days, timezone) {
2770
2787
  function createApiCommand(name, description, endpoint) {
2771
2788
  const cmd = new Command(name).description(description);
2772
2789
  cmd.command("today").description(`Today's ${name} data`).action(async (_, command) => {
2773
- const opts = command.parent.parent.opts();
2790
+ const opts = getGlobalOpts(command);
2774
2791
  const client = getClient(opts);
2775
2792
  const data = await client.fetch(endpoint, todayDate(), todayDate());
2776
2793
  console.log(JSON.stringify(data, null, 2));
2777
2794
  });
2778
2795
  cmd.command("date <day>").description(`${name} data for specific date (YYYY-MM-DD)`).action(async (day, _, command) => {
2779
- const opts = command.parent.parent.opts();
2796
+ const opts = getGlobalOpts(command);
2780
2797
  const client = getClient(opts);
2781
2798
  const data = await client.fetch(endpoint, day, day);
2782
2799
  console.log(JSON.stringify(data, null, 2));
2783
2800
  });
2784
2801
  cmd.command("week").description(`Last 7 days of ${name} data`).action(async (_, command) => {
2785
- const opts = command.parent.parent.opts();
2802
+ const opts = getGlobalOpts(command);
2786
2803
  const client = getClient(opts);
2787
2804
  const { start, end } = dateRange(7);
2788
2805
  const data = await client.fetch(endpoint, start, end);
@@ -3723,6 +3740,7 @@ function buildManifest(version) {
3723
3740
  return {
3724
3741
  name: "oura-cli",
3725
3742
  version,
3743
+ compatManifestCommand: "oura-cli manifest",
3726
3744
  auth: {
3727
3745
  envVars: ["OURA_TOKEN", "OURA_TOKEN_PATH"],
3728
3746
  tokenFile: "~/.oura-token",
@@ -3858,7 +3876,7 @@ function describeCommand(version) {
3858
3876
  }
3859
3877
 
3860
3878
  // src/index.ts
3861
- var VERSION = "0.1.3";
3879
+ var VERSION = "0.2.0";
3862
3880
  if (process.argv.includes("--no-color") || process.env.NO_COLOR) {
3863
3881
  source_default.level = 0;
3864
3882
  }
@@ -7,6 +7,7 @@
7
7
  "properties": {
8
8
  "name": { "const": "oura-cli" },
9
9
  "version": { "type": "string" },
10
+ "compatManifestCommand": { "type": "string" },
10
11
  "auth": {
11
12
  "type": "object",
12
13
  "required": ["envVars", "tokenFile", "loginCommand"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drakulavich/oura-cli",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Oura Ring CLI — query and analyze Oura Ring health data from the command line, designed for humans and AI agents.",
5
5
  "keywords": [
6
6
  "oura",