hae-vault 0.1.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/.env.example +7 -0
- package/CLAUDE.md +220 -0
- package/README.md +206 -0
- package/SKILL.md +60 -0
- package/dist/cli/dashboard.d.ts +3 -0
- package/dist/cli/dashboard.d.ts.map +1 -0
- package/dist/cli/dashboard.js +206 -0
- package/dist/cli/dashboard.js.map +1 -0
- package/dist/cli/import.d.ts +3 -0
- package/dist/cli/import.d.ts.map +1 -0
- package/dist/cli/import.js +78 -0
- package/dist/cli/import.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +31 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/info.d.ts +5 -0
- package/dist/cli/info.d.ts.map +1 -0
- package/dist/cli/info.js +34 -0
- package/dist/cli/info.js.map +1 -0
- package/dist/cli/metrics.d.ts +3 -0
- package/dist/cli/metrics.d.ts.map +1 -0
- package/dist/cli/metrics.js +20 -0
- package/dist/cli/metrics.js.map +1 -0
- package/dist/cli/query.d.ts +3 -0
- package/dist/cli/query.d.ts.map +1 -0
- package/dist/cli/query.js +18 -0
- package/dist/cli/query.js.map +1 -0
- package/dist/cli/serve.d.ts +3 -0
- package/dist/cli/serve.d.ts.map +1 -0
- package/dist/cli/serve.js +19 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/cli/sleep.d.ts +3 -0
- package/dist/cli/sleep.d.ts.map +1 -0
- package/dist/cli/sleep.js +19 -0
- package/dist/cli/sleep.js.map +1 -0
- package/dist/cli/summary.d.ts +3 -0
- package/dist/cli/summary.d.ts.map +1 -0
- package/dist/cli/summary.js +53 -0
- package/dist/cli/summary.js.map +1 -0
- package/dist/cli/trends.d.ts +3 -0
- package/dist/cli/trends.d.ts.map +1 -0
- package/dist/cli/trends.js +77 -0
- package/dist/cli/trends.js.map +1 -0
- package/dist/cli/watch.d.ts +12 -0
- package/dist/cli/watch.d.ts.map +1 -0
- package/dist/cli/watch.js +89 -0
- package/dist/cli/watch.js.map +1 -0
- package/dist/cli/workouts.d.ts +3 -0
- package/dist/cli/workouts.d.ts.map +1 -0
- package/dist/cli/workouts.js +19 -0
- package/dist/cli/workouts.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +25 -0
- package/dist/config.js.map +1 -0
- package/dist/db/importLog.d.ts +5 -0
- package/dist/db/importLog.d.ts.map +1 -0
- package/dist/db/importLog.js +10 -0
- package/dist/db/importLog.js.map +1 -0
- package/dist/db/metrics.d.ts +4 -0
- package/dist/db/metrics.d.ts.map +1 -0
- package/dist/db/metrics.js +14 -0
- package/dist/db/metrics.js.map +1 -0
- package/dist/db/schema.d.ts +5 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +100 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/sleep.d.ts +4 -0
- package/dist/db/sleep.d.ts.map +1 -0
- package/dist/db/sleep.js +13 -0
- package/dist/db/sleep.js.map +1 -0
- package/dist/db/workouts.d.ts +4 -0
- package/dist/db/workouts.d.ts.map +1 -0
- package/dist/db/workouts.js +11 -0
- package/dist/db/workouts.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/parse/metrics.d.ts +17 -0
- package/dist/parse/metrics.d.ts.map +1 -0
- package/dist/parse/metrics.js +33 -0
- package/dist/parse/metrics.js.map +1 -0
- package/dist/parse/sleep.d.ts +23 -0
- package/dist/parse/sleep.d.ts.map +1 -0
- package/dist/parse/sleep.js +58 -0
- package/dist/parse/sleep.js.map +1 -0
- package/dist/parse/time.d.ts +4 -0
- package/dist/parse/time.d.ts.map +1 -0
- package/dist/parse/time.js +41 -0
- package/dist/parse/time.js.map +1 -0
- package/dist/parse/workouts.d.ts +17 -0
- package/dist/parse/workouts.d.ts.map +1 -0
- package/dist/parse/workouts.js +24 -0
- package/dist/parse/workouts.js.map +1 -0
- package/dist/server/app.d.ts +5 -0
- package/dist/server/app.d.ts.map +1 -0
- package/dist/server/app.js +39 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/ingest.d.ts +15 -0
- package/dist/server/ingest.d.ts.map +1 -0
- package/dist/server/ingest.js +41 -0
- package/dist/server/ingest.js.map +1 -0
- package/dist/types/hae.d.ts +103 -0
- package/dist/types/hae.d.ts.map +1 -0
- package/dist/types/hae.js +2 -0
- package/dist/types/hae.js.map +1 -0
- package/dist/util/zip.d.ts +3 -0
- package/dist/util/zip.d.ts.map +1 -0
- package/dist/util/zip.js +24 -0
- package/dist/util/zip.js.map +1 -0
- package/docs/COMMANDS.md +315 -0
- package/docs/plans/2026-02-18-hae-vault-initial-implementation.md +2015 -0
- package/docs/plans/2026-02-18-readme-dashboard-design.md +213 -0
- package/docs/plans/2026-02-18-readme-dashboard-plan.md +1306 -0
- package/docs/plans/2026-02-18-zip-env-watch-design.md +213 -0
- package/docs/plans/2026-02-18-zip-env-watch.md +966 -0
- package/package.json +57 -0
- package/src/cli/dashboard.ts +242 -0
- package/src/cli/import.ts +85 -0
- package/src/cli/index.ts +32 -0
- package/src/cli/info.ts +36 -0
- package/src/cli/metrics.ts +20 -0
- package/src/cli/query.ts +17 -0
- package/src/cli/serve.ts +18 -0
- package/src/cli/sleep.ts +19 -0
- package/src/cli/summary.ts +58 -0
- package/src/cli/trends.ts +103 -0
- package/src/cli/watch.ts +111 -0
- package/src/cli/workouts.ts +19 -0
- package/src/config.ts +28 -0
- package/src/db/importLog.ts +18 -0
- package/src/db/metrics.ts +15 -0
- package/src/db/schema.ts +105 -0
- package/src/db/sleep.ts +15 -0
- package/src/db/workouts.ts +13 -0
- package/src/index.ts +4 -0
- package/src/parse/metrics.ts +50 -0
- package/src/parse/sleep.ts +82 -0
- package/src/parse/time.ts +43 -0
- package/src/parse/workouts.ts +42 -0
- package/src/server/app.ts +46 -0
- package/src/server/ingest.ts +68 -0
- package/src/types/hae.ts +94 -0
- package/src/util/zip.ts +24 -0
- package/tests/cli-watch.test.ts +64 -0
- package/tests/db-import-log.test.ts +40 -0
- package/tests/db-metrics.test.ts +44 -0
- package/tests/db-schema.test.ts +55 -0
- package/tests/db-sleep.test.ts +36 -0
- package/tests/db-workouts.test.ts +34 -0
- package/tests/ingest.test.ts +99 -0
- package/tests/parse-metrics.test.ts +55 -0
- package/tests/parse-sleep.test.ts +65 -0
- package/tests/parse-time.test.ts +48 -0
- package/tests/parse-workouts.test.ts +43 -0
- package/tests/types.test.ts +27 -0
- package/tests/util-zip.test.ts +46 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Design: ZIP support, env config, watch command, import deduplication
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-02-18
|
|
4
|
+
**Status:** Approved
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Summary
|
|
9
|
+
|
|
10
|
+
Four additions to the existing `hae-vault` codebase:
|
|
11
|
+
|
|
12
|
+
1. **ZIP support** in `hvault import` — extract `HealthAutoExport-*.json` from a `.zip`, ignore `.gpx`
|
|
13
|
+
2. **Env config** via `.env` + env vars — full Docker-compatible configuration
|
|
14
|
+
3. **`hvault watch`** — polls a directory, auto-imports new HAE exports
|
|
15
|
+
4. **`import_log` table + SHA-256 dedup** — skip already-imported files before parsing
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 1. Env Config (`src/config.ts`)
|
|
20
|
+
|
|
21
|
+
New module, loaded once at process startup via a top-level import in `src/index.ts`.
|
|
22
|
+
|
|
23
|
+
### Load order (highest priority first)
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
CLI flag > env var (process.env) > .env file > hardcoded default
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Dotenv loads from:
|
|
30
|
+
1. `HVAULT_ENV_FILE` env var if set (explicit path to `.env`)
|
|
31
|
+
2. `./.env` in CWD (local dev convenience)
|
|
32
|
+
|
|
33
|
+
In Docker: set env vars directly — no `.env` file needed.
|
|
34
|
+
|
|
35
|
+
### Variables
|
|
36
|
+
|
|
37
|
+
| Env var | Default | Used by |
|
|
38
|
+
|-----------------------|------------------------------|----------------------|
|
|
39
|
+
| `HVAULT_ENV_FILE` | — | config bootstrap |
|
|
40
|
+
| `HVAULT_DB_PATH` | `~/.hae-vault/health.db` | all commands |
|
|
41
|
+
| `HVAULT_PORT` | `4242` | `serve` |
|
|
42
|
+
| `HVAULT_TOKEN` | — | `serve` |
|
|
43
|
+
| `HVAULT_WATCH_DIR` | — | `watch` |
|
|
44
|
+
| `HVAULT_WATCH_INTERVAL` | `60` | `watch` |
|
|
45
|
+
| `HVAULT_TARGET` | `default` | `import`, `watch` |
|
|
46
|
+
|
|
47
|
+
### Tilde expansion
|
|
48
|
+
|
|
49
|
+
`HVAULT_DB_PATH=~/.hae-vault/health.db` — dotenv reads `~` as a literal character. `config.ts` replaces a leading `~/` with `os.homedir() + '/'`.
|
|
50
|
+
|
|
51
|
+
### config object
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
export const config = {
|
|
55
|
+
dbPath: string, // resolved absolute path
|
|
56
|
+
port: number,
|
|
57
|
+
token: string | undefined,
|
|
58
|
+
watchDir: string | undefined,
|
|
59
|
+
watchInterval: number, // seconds
|
|
60
|
+
target: string,
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### CLI flag override pattern
|
|
65
|
+
|
|
66
|
+
Each command reads its default from `config`, CLI flags override:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// serve.ts
|
|
70
|
+
.option('-p, --port <number>', 'Port', String(config.port))
|
|
71
|
+
.option('--token <secret>', 'Bearer token', config.token)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 2. ZIP support in `hvault import`
|
|
77
|
+
|
|
78
|
+
Library: **`adm-zip`** — pure JS, synchronous, no native bindings. Matches existing sync codebase.
|
|
79
|
+
|
|
80
|
+
### Logic in `src/cli/import.ts`
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
if file.endsWith('.zip'):
|
|
84
|
+
open zip
|
|
85
|
+
find entry matching /HealthAutoExport.*\.json$/i
|
|
86
|
+
if not found → error "No HealthAutoExport-*.json found in zip"
|
|
87
|
+
read entry buffer → parse as JSON
|
|
88
|
+
else:
|
|
89
|
+
readFileSync(file) → parse as JSON
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
GPX files are ignored entirely — no extraction, no error.
|
|
93
|
+
|
|
94
|
+
### Hash computation
|
|
95
|
+
|
|
96
|
+
SHA-256 of the **raw file bytes** (the `.zip` or `.json` file itself, before extraction). Computed with `node:crypto` — no extra dependency.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 3. `import_log` table (deduplication)
|
|
101
|
+
|
|
102
|
+
### Schema addition to `src/db/schema.ts`
|
|
103
|
+
|
|
104
|
+
```sql
|
|
105
|
+
CREATE TABLE IF NOT EXISTS import_log (
|
|
106
|
+
id INTEGER PRIMARY KEY,
|
|
107
|
+
filename TEXT NOT NULL,
|
|
108
|
+
file_hash TEXT NOT NULL UNIQUE, -- SHA-256 hex of file content
|
|
109
|
+
imported_at TEXT NOT NULL, -- ISO8601
|
|
110
|
+
metrics_added INTEGER,
|
|
111
|
+
sleep_added INTEGER,
|
|
112
|
+
workouts_added INTEGER
|
|
113
|
+
);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Dedup flow (in `src/cli/import.ts`)
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
1. hash = sha256(fileBuffer)
|
|
120
|
+
2. existing = db.prepare('SELECT id FROM import_log WHERE file_hash = ?').get(hash)
|
|
121
|
+
3. if existing → print { skipped: true, reason: 'already imported', hash } → exit 0
|
|
122
|
+
4. ingest(payload)
|
|
123
|
+
5. INSERT INTO import_log (filename, file_hash, imported_at, metrics_added, sleep_added, workouts_added)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### `ingest()` return type change
|
|
127
|
+
|
|
128
|
+
`ingest()` currently returns `void`. Change to return `IngestResult`:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
export interface IngestResult {
|
|
132
|
+
metricsAdded: number;
|
|
133
|
+
sleepAdded: number;
|
|
134
|
+
workoutsAdded: number;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
This is a breaking change to the function signature — existing `ingest.test.ts` will need updating (callers that ignore the return value are fine; tests asserting `void` need a minor fix).
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## 4. `hvault watch` command (`src/cli/watch.ts`)
|
|
143
|
+
|
|
144
|
+
Polls a directory on an interval, picks up new HAE export files, imports them.
|
|
145
|
+
|
|
146
|
+
### File matching pattern
|
|
147
|
+
|
|
148
|
+
`HealthAutoExport-*.{zip,json}` — case-insensitive regex: `/^HealthAutoExport.*\.(zip|json)$/i`
|
|
149
|
+
|
|
150
|
+
### Flow
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
startup:
|
|
154
|
+
resolve watchDir (--dir flag ?? HVAULT_WATCH_DIR ?? error)
|
|
155
|
+
open DB
|
|
156
|
+
log "Watching <dir> every <N>s"
|
|
157
|
+
|
|
158
|
+
each tick:
|
|
159
|
+
list files in dir matching pattern
|
|
160
|
+
for each file:
|
|
161
|
+
hash = sha256(readFileSync(file))
|
|
162
|
+
if import_log has this hash → skip
|
|
163
|
+
import(file) → log result
|
|
164
|
+
sleep N seconds → repeat
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Options
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
hvault watch [--dir <path>] [--interval <seconds>] [--target <name>]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Defaults from `config.watchDir`, `config.watchInterval`, `config.target`.
|
|
174
|
+
|
|
175
|
+
`--dir` is required if `HVAULT_WATCH_DIR` is not set — error and exit if neither provided.
|
|
176
|
+
|
|
177
|
+
### Output
|
|
178
|
+
|
|
179
|
+
Each tick logs to stdout as JSON lines:
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{ "tick": "2026-02-18T10:00:00Z", "dir": "/Downloads", "found": 3, "imported": 1, "skipped": 2 }
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
On import, also logs the import result (same format as `hvault import`).
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 5. Files changed / created
|
|
190
|
+
|
|
191
|
+
| File | Change |
|
|
192
|
+
|------|--------|
|
|
193
|
+
| `package.json` | add `adm-zip` dependency |
|
|
194
|
+
| `src/config.ts` | NEW — dotenv load + config singleton |
|
|
195
|
+
| `src/index.ts` | add `import './config.js'` at top |
|
|
196
|
+
| `src/db/schema.ts` | add `import_log` table |
|
|
197
|
+
| `src/server/ingest.ts` | return `IngestResult` instead of `void` |
|
|
198
|
+
| `src/cli/import.ts` | ZIP support + hash dedup + `import_log` write |
|
|
199
|
+
| `src/cli/serve.ts` | read port/token defaults from `config` |
|
|
200
|
+
| `src/cli/watch.ts` | NEW |
|
|
201
|
+
| `src/cli/index.ts` | register `watchCommand` |
|
|
202
|
+
| `tests/ingest.test.ts` | update for `IngestResult` return type |
|
|
203
|
+
| `tests/import-zip.test.ts` | NEW — test ZIP extraction + dedup |
|
|
204
|
+
| `tests/watch.test.ts` | NEW — test watch polling logic |
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Out of scope
|
|
209
|
+
|
|
210
|
+
- File deletion after import (watch leaves files in place)
|
|
211
|
+
- Recursive directory scanning (flat dir only)
|
|
212
|
+
- `.env` file creation tooling
|
|
213
|
+
- Docker Compose / Dockerfile (not part of this package)
|