hae-vault 0.1.0 → 0.2.1
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/README.md +12 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +3 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/start.d.ts +3 -0
- package/dist/cli/start.d.ts.map +1 -0
- package/dist/cli/start.js +22 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/package.json +5 -1
- package/.env.example +0 -7
- package/CLAUDE.md +0 -220
- package/SKILL.md +0 -60
- package/docs/COMMANDS.md +0 -315
- package/docs/plans/2026-02-18-hae-vault-initial-implementation.md +0 -2015
- package/docs/plans/2026-02-18-readme-dashboard-design.md +0 -213
- package/docs/plans/2026-02-18-readme-dashboard-plan.md +0 -1306
- package/docs/plans/2026-02-18-zip-env-watch-design.md +0 -213
- package/docs/plans/2026-02-18-zip-env-watch.md +0 -966
- package/src/cli/dashboard.ts +0 -242
- package/src/cli/import.ts +0 -85
- package/src/cli/index.ts +0 -32
- package/src/cli/info.ts +0 -36
- package/src/cli/metrics.ts +0 -20
- package/src/cli/query.ts +0 -17
- package/src/cli/serve.ts +0 -18
- package/src/cli/sleep.ts +0 -19
- package/src/cli/summary.ts +0 -58
- package/src/cli/trends.ts +0 -103
- package/src/cli/watch.ts +0 -111
- package/src/cli/workouts.ts +0 -19
- package/src/config.ts +0 -28
- package/src/db/importLog.ts +0 -18
- package/src/db/metrics.ts +0 -15
- package/src/db/schema.ts +0 -105
- package/src/db/sleep.ts +0 -15
- package/src/db/workouts.ts +0 -13
- package/src/index.ts +0 -4
- package/src/parse/metrics.ts +0 -50
- package/src/parse/sleep.ts +0 -82
- package/src/parse/time.ts +0 -43
- package/src/parse/workouts.ts +0 -42
- package/src/server/app.ts +0 -46
- package/src/server/ingest.ts +0 -68
- package/src/types/hae.ts +0 -94
- package/src/util/zip.ts +0 -24
- package/tests/cli-watch.test.ts +0 -64
- package/tests/db-import-log.test.ts +0 -40
- package/tests/db-metrics.test.ts +0 -44
- package/tests/db-schema.test.ts +0 -55
- package/tests/db-sleep.test.ts +0 -36
- package/tests/db-workouts.test.ts +0 -34
- package/tests/ingest.test.ts +0 -99
- package/tests/parse-metrics.test.ts +0 -55
- package/tests/parse-sleep.test.ts +0 -65
- package/tests/parse-time.test.ts +0 -48
- package/tests/parse-workouts.test.ts +0 -43
- package/tests/types.test.ts +0 -27
- package/tests/util-zip.test.ts +0 -46
- package/tsconfig.json +0 -19
package/README.md
CHANGED
|
@@ -21,6 +21,9 @@ hvault summary --color # N-day averages with emoji indicators
|
|
|
21
21
|
|
|
22
22
|
1. Install the [Health Auto Export](https://www.healthexportapp.com) iOS app
|
|
23
23
|
2. In HAE: Settings → REST API → set server URL to `http://your-server:4242/api/ingest`
|
|
24
|
+
- Endpoint: `POST /api/ingest` (JSON body, max 50mb)
|
|
25
|
+
- Health check: `GET /health` → `{"status":"ok"}`
|
|
26
|
+
- Optional: append `?target=me` to tag data by device/person
|
|
24
27
|
3. Or: export a ZIP from HAE and run `hvault import export.zip`
|
|
25
28
|
|
|
26
29
|
**Optional:** create `.env` in your working directory to override defaults:
|
|
@@ -29,7 +32,9 @@ hvault summary --color # N-day averages with emoji indicators
|
|
|
29
32
|
HVAULT_DB_PATH=~/.hae-vault/health.db # SQLite DB location
|
|
30
33
|
HVAULT_PORT=4242 # ingest server port
|
|
31
34
|
HVAULT_TOKEN=secret # bearer token for serve (optional)
|
|
35
|
+
HVAULT_AUTH=secret # alias for HVAULT_TOKEN (use if _TOKEN is blocked)
|
|
32
36
|
HVAULT_WATCH_DIR=~/Downloads # directory to watch for exports
|
|
37
|
+
HVAULT_WATCH_INTERVAL=60 # watch poll interval in seconds (default: 60)
|
|
33
38
|
```
|
|
34
39
|
|
|
35
40
|
## Commands
|
|
@@ -38,10 +43,15 @@ HVAULT_WATCH_DIR=~/Downloads # directory to watch for exports
|
|
|
38
43
|
|
|
39
44
|
| Command | Description |
|
|
40
45
|
| --- | --- |
|
|
41
|
-
| `hvault
|
|
46
|
+
| `hvault start` | Start server + watcher based on env vars (recommended for Docker/daemons) |
|
|
47
|
+
| `hvault serve` | Start HTTP server only |
|
|
42
48
|
| `hvault import <file>` | Import HAE JSON or ZIP export (idempotent) |
|
|
43
49
|
| `hvault watch` | Watch directory and auto-import new HAE exports |
|
|
44
50
|
|
|
51
|
+
> **`watch` file matching:** only files matching `/^HealthAutoExport.*\.(zip|json)$/i` are picked up.
|
|
52
|
+
> The Health Auto Export app names files this way by default (e.g. `HealthAutoExport-2020-2025.zip`).
|
|
53
|
+
> Files with any other name are silently ignored.
|
|
54
|
+
|
|
45
55
|
### Query
|
|
46
56
|
|
|
47
57
|
Output is JSON by default. Add `--pretty` for formatted JSON.
|
|
@@ -168,6 +178,7 @@ Load order: CLI flag > env var > `.env` file > default.
|
|
|
168
178
|
HVAULT_DB_PATH=~/.hae-vault/health.db # SQLite DB location
|
|
169
179
|
HVAULT_PORT=4242 # serve port
|
|
170
180
|
HVAULT_TOKEN=secret # bearer token for serve
|
|
181
|
+
HVAULT_AUTH=secret # alias for HVAULT_TOKEN (use if _TOKEN is blocked by your env)
|
|
171
182
|
HVAULT_WATCH_DIR=~/Downloads # directory to watch
|
|
172
183
|
HVAULT_WATCH_INTERVAL=60 # watch poll interval (seconds)
|
|
173
184
|
HVAULT_TARGET=default # default target name
|
package/dist/cli/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,eAAO,MAAM,OAAO,SAAgB,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -10,11 +10,13 @@ import { importCommand } from './import.js';
|
|
|
10
10
|
import { watchCommand } from './watch.js';
|
|
11
11
|
import { dashboardCommand } from './dashboard.js';
|
|
12
12
|
import { trendsCommand } from './trends.js';
|
|
13
|
+
import { startCommand } from './start.js';
|
|
13
14
|
export const program = new Command();
|
|
14
15
|
program
|
|
15
16
|
.name('hvault')
|
|
16
17
|
.description('Apple Health data vault — ingest + query')
|
|
17
|
-
.version('0.
|
|
18
|
+
.version('0.2.0');
|
|
19
|
+
program.addCommand(startCommand);
|
|
18
20
|
program.addCommand(serveCommand);
|
|
19
21
|
program.addCommand(importCommand);
|
|
20
22
|
program.addCommand(watchCommand);
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AACrC,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,0CAA0C,CAAC;KACvD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;AACrC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/cli/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,YAAY,SAiBrB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { createApp } from '../server/app.js';
|
|
3
|
+
import { openDb } from '../db/schema.js';
|
|
4
|
+
import { tick } from './watch.js';
|
|
5
|
+
import { config } from '../config.js';
|
|
6
|
+
export const startCommand = new Command('start')
|
|
7
|
+
.description('Start server and watcher based on environment variables (HVAULT_PORT, HVAULT_WATCH_DIR)')
|
|
8
|
+
.action(() => {
|
|
9
|
+
const db = openDb(config.dbPath);
|
|
10
|
+
// Always start HTTP server
|
|
11
|
+
const app = createApp(db, { token: config.token });
|
|
12
|
+
app.listen(config.port, () => {
|
|
13
|
+
console.log(JSON.stringify({ server: `http://0.0.0.0:${config.port}/api/ingest`, auth: !!config.token }));
|
|
14
|
+
});
|
|
15
|
+
// Start watcher only if HVAULT_WATCH_DIR is set
|
|
16
|
+
if (config.watchDir) {
|
|
17
|
+
console.log(JSON.stringify({ watching: config.watchDir, intervalSeconds: config.watchInterval, target: config.target }));
|
|
18
|
+
tick(db, config.watchDir, config.target);
|
|
19
|
+
setInterval(() => tick(db, config.watchDir, config.target), config.watchInterval * 1000);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
//# sourceMappingURL=start.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/cli/start.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,yFAAyF,CAAC;KACtG,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAEjC,2BAA2B;IAC3B,MAAM,GAAG,GAAG,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IACnD,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;QAC3B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,kBAAkB,MAAM,CAAC,IAAI,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5G,CAAC,CAAC,CAAC;IAEH,gDAAgD;IAChD,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,eAAe,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACzH,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACzC,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,QAAS,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC5F,CAAC;AACH,CAAC,CAAC,CAAC"}
|
package/dist/config.js
CHANGED
|
@@ -17,7 +17,7 @@ const DEFAULT_DB_PATH = join(homedir(), '.hae-vault', 'health.db');
|
|
|
17
17
|
export const config = {
|
|
18
18
|
dbPath: expandTilde(process.env.HVAULT_DB_PATH ?? DEFAULT_DB_PATH),
|
|
19
19
|
port: Number(process.env.HVAULT_PORT ?? 4242),
|
|
20
|
-
token: process.env.HVAULT_TOKEN,
|
|
20
|
+
token: process.env.HVAULT_TOKEN ?? process.env.HVAULT_AUTH,
|
|
21
21
|
watchDir: process.env.HVAULT_WATCH_DIR ? expandTilde(process.env.HVAULT_WATCH_DIR) : undefined,
|
|
22
22
|
watchInterval: Number(process.env.HVAULT_WATCH_INTERVAL ?? 60),
|
|
23
23
|
target: process.env.HVAULT_TARGET ?? 'default',
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,SAAS,WAAW,CAAC,CAAS;IAC5B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,OAAO,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,qEAAqE;AACrE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;AAC3E,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;AAEnE,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,MAAM,EAAS,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,eAAe,CAAC;IACzE,IAAI,EAAW,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC;IACtD,KAAK,EAAU,OAAO,CAAC,GAAG,CAAC,YAAY;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,SAAS,WAAW,CAAC,CAAS;IAC5B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,OAAO,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,qEAAqE;AACrE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;AAC3E,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;AAEnE,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,MAAM,EAAS,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,eAAe,CAAC;IACzE,IAAI,EAAW,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC;IACtD,KAAK,EAAU,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW;IAClE,QAAQ,EAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS;IACnG,aAAa,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,EAAE,CAAC;IAC9D,MAAM,EAAS,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,SAAS;CAC7C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hae-vault",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "CLI + HTTP server for Apple Health data from Health Auto Export",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"hvault": "./dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"main": "./dist/index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
10
14
|
"scripts": {
|
|
11
15
|
"dev": "tsx src/index.ts",
|
|
12
16
|
"build": "tsc",
|
package/.env.example
DELETED
package/CLAUDE.md
DELETED
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
# hae-vault — Claude Code Guide
|
|
2
|
-
|
|
3
|
-
## Project Identity
|
|
4
|
-
|
|
5
|
-
- **npm package name:** `hae-vault`
|
|
6
|
-
- **CLI command:** `hvault`
|
|
7
|
-
- **Install:** `npm install -g hae-vault`
|
|
8
|
-
- **Language:** TypeScript (Node.js 22+, ESM, NodeNext)
|
|
9
|
-
- **Status:** Implemented and working — 570k+ rows ingested from real Apple Health data
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## What This Project Does
|
|
14
|
-
|
|
15
|
-
`hae-vault` is an npm package with three responsibilities:
|
|
16
|
-
|
|
17
|
-
1. **Server** (`hvault serve`): HTTP server that receives health data pushed from the **Health Auto Export** iOS app. POST to `/api/ingest`, writes to local SQLite.
|
|
18
|
-
|
|
19
|
-
2. **Import** (`hvault import <file>`): Bulk import from a HAE JSON or ZIP export file. Idempotent via SHA-256 hash tracking — skips already-imported files.
|
|
20
|
-
|
|
21
|
-
3. **Watch** (`hvault watch`): Polls a directory for new HAE export files, auto-imports on schedule. Configurable via env vars.
|
|
22
|
-
|
|
23
|
-
4. **CLI** (`hvault <command>`): Query interface for AI agents (OpenClaw). Returns JSON.
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Ecosystem Context
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
OpenClaw (AI agent platform, remote server)
|
|
31
|
-
├── skill: whoop-up ← existing, WHOOP wearable, live API calls
|
|
32
|
-
├── skill: hae-vault ← Apple Health archive, queries SQLite
|
|
33
|
-
│
|
|
34
|
-
├── whoop-up CLI ← npm install -g whoop-up (already published)
|
|
35
|
-
└── hvault CLI ← npm install -g hae-vault (local install)
|
|
36
|
-
└── reads ~/.hae-vault/health.db (SQLite)
|
|
37
|
-
|
|
38
|
-
iPhone (Health Auto Export app)
|
|
39
|
-
└── REST API automation → POST http://server:4242/api/ingest
|
|
40
|
-
└── writes to health.db via hvault serve
|
|
41
|
-
|
|
42
|
-
OR
|
|
43
|
-
|
|
44
|
-
HAE Manual Export → .zip file → hvault import export.zip
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
## Source Structure
|
|
50
|
-
|
|
51
|
-
```
|
|
52
|
-
hae-vault/
|
|
53
|
-
├── src/
|
|
54
|
-
│ ├── index.ts ← entry point, Commander CLI
|
|
55
|
-
│ ├── config.ts ← env config singleton (dotenv + HVAULT_* vars)
|
|
56
|
-
│ ├── server/
|
|
57
|
-
│ │ ├── app.ts ← Express HTTP server
|
|
58
|
-
│ │ └── ingest.ts ← parse payload → write to DB, returns IngestResult
|
|
59
|
-
│ ├── db/
|
|
60
|
-
│ │ ├── schema.ts ← SQLite schema + openDb() + closeDb()
|
|
61
|
-
│ │ ├── metrics.ts ← upsertMetrics()
|
|
62
|
-
│ │ ├── sleep.ts ← upsertSleep()
|
|
63
|
-
│ │ ├── workouts.ts ← upsertWorkout()
|
|
64
|
-
│ │ └── importLog.ts ← hasBeenImported() + logImport() (SHA-256 dedup)
|
|
65
|
-
│ ├── parse/
|
|
66
|
-
│ │ ├── time.ts ← 5-format date parser
|
|
67
|
-
│ │ ├── metrics.ts ← MetricData[] → NormalizedMetric[]
|
|
68
|
-
│ │ ├── sleep.ts ← detect 3 variants, normalizeSleep()
|
|
69
|
-
│ │ └── workouts.ts ← WorkoutData[] → NormalizedWorkout
|
|
70
|
-
│ ├── util/
|
|
71
|
-
│ │ └── zip.ts ← extractPayloadFromZip() — adm-zip, finds HealthAutoExport-*.json
|
|
72
|
-
│ ├── cli/
|
|
73
|
-
│ │ ├── index.ts ← program + all command registrations
|
|
74
|
-
│ │ ├── serve.ts ← hvault serve
|
|
75
|
-
│ │ ├── import.ts ← hvault import <file> (JSON or ZIP, dedup via import_log)
|
|
76
|
-
│ │ ├── watch.ts ← hvault watch (polls dir, auto-imports, exports tick())
|
|
77
|
-
│ │ ├── metrics.ts ← hvault metrics --metric <name> --days N
|
|
78
|
-
│ │ ├── sleep.ts ← hvault sleep --days N
|
|
79
|
-
│ │ ├── workouts.ts ← hvault workouts --days N
|
|
80
|
-
│ │ ├── summary.ts ← hvault summary --days N
|
|
81
|
-
│ │ ├── query.ts ← hvault query "<sql>"
|
|
82
|
-
│ │ └── info.ts ← hvault sources | last-sync | stats
|
|
83
|
-
│ └── types/
|
|
84
|
-
│ └── hae.ts ← TypeScript interfaces for HAE payload
|
|
85
|
-
├── tests/
|
|
86
|
-
│ ├── types.test.ts
|
|
87
|
-
│ ├── parse-time.test.ts
|
|
88
|
-
│ ├── parse-sleep.test.ts
|
|
89
|
-
│ ├── parse-metrics.test.ts
|
|
90
|
-
│ ├── parse-workouts.test.ts
|
|
91
|
-
│ ├── db-schema.test.ts
|
|
92
|
-
│ ├── db-metrics.test.ts
|
|
93
|
-
│ ├── db-sleep.test.ts
|
|
94
|
-
│ ├── db-workouts.test.ts
|
|
95
|
-
│ ├── db-import-log.test.ts
|
|
96
|
-
│ ├── ingest.test.ts
|
|
97
|
-
│ ├── util-zip.test.ts
|
|
98
|
-
│ └── cli-watch.test.ts
|
|
99
|
-
├── docs/plans/ ← design + implementation plan docs
|
|
100
|
-
├── CLAUDE.md
|
|
101
|
-
├── SKILL.md ← OpenClaw skill definition
|
|
102
|
-
├── package.json ← bin: { "hvault": "dist/index.js" }
|
|
103
|
-
└── tsconfig.json
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## CLI Commands
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
# Server
|
|
112
|
-
hvault serve # start HTTP ingest server (default port 4242)
|
|
113
|
-
hvault serve --port 4242 # custom port
|
|
114
|
-
hvault serve --token <secret> # require Authorization: Bearer <secret>
|
|
115
|
-
|
|
116
|
-
# Import from file (JSON or ZIP)
|
|
117
|
-
hvault import export.json # import HAE JSON export
|
|
118
|
-
hvault import export.zip # import HAE zip (extracts HealthAutoExport-*.json)
|
|
119
|
-
hvault import export.zip --target me # tag with device/person name
|
|
120
|
-
|
|
121
|
-
# Watch directory for new exports
|
|
122
|
-
hvault watch # uses HVAULT_WATCH_DIR env var
|
|
123
|
-
hvault watch --dir ~/Downloads # watch specific directory
|
|
124
|
-
hvault watch --interval 60 # check every 60 seconds
|
|
125
|
-
|
|
126
|
-
# Query (all return JSON, --pretty for formatted)
|
|
127
|
-
hvault metrics --metric step_count --days 30
|
|
128
|
-
hvault metrics --metric heart_rate --days 7
|
|
129
|
-
hvault sleep --days 14
|
|
130
|
-
hvault workouts --days 30
|
|
131
|
-
hvault summary --days 90
|
|
132
|
-
hvault query "<sql>" # raw SQL → JSON
|
|
133
|
-
hvault sources # metric coverage in DB
|
|
134
|
-
hvault last-sync # last REST API push received
|
|
135
|
-
hvault stats # row counts per table
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## Environment Variables
|
|
141
|
-
|
|
142
|
-
Load order: CLI flag > env var > `.env` file > hardcoded default.
|
|
143
|
-
|
|
144
|
-
In Docker: set env vars directly — no `.env` file needed.
|
|
145
|
-
|
|
146
|
-
```bash
|
|
147
|
-
HVAULT_ENV_FILE=/path/to/.env # override .env file location (default: CWD/.env)
|
|
148
|
-
HVAULT_DB_PATH=~/.hae-vault/health.db # SQLite DB location (tilde expanded)
|
|
149
|
-
HVAULT_PORT=4242 # serve port
|
|
150
|
-
HVAULT_TOKEN=secret # bearer token for serve
|
|
151
|
-
HVAULT_WATCH_DIR=~/Downloads # directory to watch for exports (tilde expanded)
|
|
152
|
-
HVAULT_WATCH_INTERVAL=60 # watch poll interval (seconds)
|
|
153
|
-
HVAULT_TARGET=default # default target name
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## SQLite Schema
|
|
159
|
-
|
|
160
|
-
Database: `~/.hae-vault/health.db`
|
|
161
|
-
|
|
162
|
-
### Tables
|
|
163
|
-
- `metrics` — all health metrics (steps, HR, HRV, etc.) `UNIQUE(ts, metric, source, target)`
|
|
164
|
-
- `sleep` — nightly sleep records (3 schema variants handled) `UNIQUE(date, source, target)`
|
|
165
|
-
- `workouts` — workout sessions `UNIQUE(ts, name, target)`
|
|
166
|
-
- `sync_log` — REST API push history
|
|
167
|
-
- `import_log` — file import history with SHA-256 hash for deduplication `UNIQUE(file_hash)`
|
|
168
|
-
|
|
169
|
-
### Key implementation notes
|
|
170
|
-
- **WAL mode** — concurrent reads while server writes
|
|
171
|
-
- **`INSERT OR REPLACE`** — idempotent upserts throughout
|
|
172
|
-
- **`import_log` hash check** — skip re-importing identical files
|
|
173
|
-
- **DB path** from `HVAULT_DB_PATH` env or `~/.hae-vault/health.db`
|
|
174
|
-
|
|
175
|
-
---
|
|
176
|
-
|
|
177
|
-
## Payload Format (HAE REST API)
|
|
178
|
-
|
|
179
|
-
Top-level:
|
|
180
|
-
```json
|
|
181
|
-
{ "data": { "metrics": [], "workouts": [], "stateOfMind": [], ... } }
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
### Date format — 5 variants (all handled by `src/parse/time.ts`)
|
|
185
|
-
```
|
|
186
|
-
"2026-01-15 14:30:00 +0000" ← 24-hour
|
|
187
|
-
"2026-01-15 2:30:00 PM +0000" ← 12-hour uppercase
|
|
188
|
-
"2026-01-15 2:30:00 pm +0000" ← 12-hour lowercase
|
|
189
|
-
"2026-01-15 2:30:00\u202fPM +0000" ← narrow non-breaking space before PM
|
|
190
|
-
"2026-01-15 2:30:00\u202fpm +0000" ← narrow non-breaking space before pm
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
### Sleep — 3 schema variants (detected by `src/parse/sleep.ts`)
|
|
194
|
-
- `detailed` — has `startDate`/`endDate` (non-aggregated phases)
|
|
195
|
-
- `aggregated_v2` — has `core`/`deep`/`rem` + `source` (HAE >= 6.6.2)
|
|
196
|
-
- `aggregated_v1` — has `sleepSource`/`inBedSource` (HAE < 6.6.2)
|
|
197
|
-
|
|
198
|
-
Detection: `'startDate' in dp` → detailed | `'core' in dp` → v2 | else → v1
|
|
199
|
-
|
|
200
|
-
---
|
|
201
|
-
|
|
202
|
-
## Development
|
|
203
|
-
|
|
204
|
-
```bash
|
|
205
|
-
npm install
|
|
206
|
-
npm run dev -- serve # run server without building
|
|
207
|
-
npm run build # compile TypeScript → dist/
|
|
208
|
-
npm test # run all tests (59 passing)
|
|
209
|
-
npm install -g . # install globally as hvault
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
---
|
|
213
|
-
|
|
214
|
-
## Reference Projects
|
|
215
|
-
|
|
216
|
-
Located at `/Volumes/storage/01_Projects/whoop/healthy/`:
|
|
217
|
-
|
|
218
|
-
- `health-auto-export-server/` — TypeScript/MongoDB reference. Useful: `MetricName.ts`, `Metric.ts`
|
|
219
|
-
- `apple-health-ingester/` — Go reference. Useful: `types.go` (canonical type defs + time parser)
|
|
220
|
-
- `restapi.md` — Official HAE REST API documentation
|
package/SKILL.md
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: hae-vault
|
|
3
|
-
description: >
|
|
4
|
-
Apple Health archive database. Use for: historical Apple Health data (steps,
|
|
5
|
-
heart rate, HRV, sleep, workouts, mindfulness, respiratory rate, blood oxygen
|
|
6
|
-
from iPhone/Apple Watch), multi-day trends, long-term patterns. Data comes
|
|
7
|
-
from Health Auto Export iOS app synced to local SQLite. NOT for WHOOP data —
|
|
8
|
-
use whoop-up skill for that. NOT for live/real-time data.
|
|
9
|
-
metadata:
|
|
10
|
-
openclaw:
|
|
11
|
-
emoji: "🍎"
|
|
12
|
-
requires:
|
|
13
|
-
bins:
|
|
14
|
-
- hvault
|
|
15
|
-
install:
|
|
16
|
-
- id: node
|
|
17
|
-
kind: node
|
|
18
|
-
package: hae-vault
|
|
19
|
-
bins:
|
|
20
|
-
- hvault
|
|
21
|
-
label: "Install hae-vault (node)"
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
# hae-vault
|
|
25
|
-
|
|
26
|
-
Query Apple Health data stored locally by `hvault serve` from the Health Auto Export iOS app.
|
|
27
|
-
|
|
28
|
-
## Commands
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
# Query last 30 days of steps
|
|
32
|
-
hvault metrics --metric step_count --days 30
|
|
33
|
-
|
|
34
|
-
# Query HRV
|
|
35
|
-
hvault metrics --metric heart_rate_variability --days 30
|
|
36
|
-
|
|
37
|
-
# Query sleep (last 14 nights)
|
|
38
|
-
hvault sleep --days 14
|
|
39
|
-
|
|
40
|
-
# Query workouts (last 30 days)
|
|
41
|
-
hvault workouts --days 30
|
|
42
|
-
|
|
43
|
-
# Summary averages across all metrics (90 days)
|
|
44
|
-
hvault summary --days 90
|
|
45
|
-
|
|
46
|
-
# Raw SQL for custom queries
|
|
47
|
-
hvault query "SELECT date, qty FROM metrics WHERE metric='step_count' ORDER BY date DESC LIMIT 7"
|
|
48
|
-
|
|
49
|
-
# What's in the DB?
|
|
50
|
-
hvault sources
|
|
51
|
-
hvault last-sync
|
|
52
|
-
hvault stats
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Available Metrics (common)
|
|
56
|
-
|
|
57
|
-
step_count, heart_rate, heart_rate_variability, resting_heart_rate,
|
|
58
|
-
active_energy, basal_energy_burned, respiratory_rate, blood_oxygen_saturation,
|
|
59
|
-
weight_body_mass, body_fat_percentage, sleep_analysis (via hvault sleep),
|
|
60
|
-
mindful_minutes, vo2max, walking_running_distance, flights_climbed
|