@druumen/sessions-db 0.1.0 → 0.1.4
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 +297 -1
- package/README.md +28 -0
- package/cli/sessions-db-session-start-main.mjs +50 -12
- package/lib/index.cjs +1699 -0
- package/package.json +28 -7
- package/types/index.d.cts +62 -0
- package/types/index.d.ts +30 -96
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,254 @@ All notable changes to `@druumen/sessions-db` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.4] — 2026-05-16
|
|
9
|
+
|
|
10
|
+
Hook gate relaxation so marketplace cockpit users on non-druumen
|
|
11
|
+
workspaces can actually record sessions.
|
|
12
|
+
|
|
13
|
+
### Changed (hook)
|
|
14
|
+
|
|
15
|
+
- `cli/sessions-db-session-start-main.mjs` — the cwd-gate accepts a
|
|
16
|
+
workspace as authorized when EITHER:
|
|
17
|
+
1. a `CLAUDE.md` containing the `Druumen Workspace` sentinel exists
|
|
18
|
+
at cwd or any ancestor (original 0.1.x behavior), OR
|
|
19
|
+
2. `.dru-code/sessions-db.json` or `tickets/_logs/sessions-db.json`
|
|
20
|
+
exists at cwd or any ancestor — i.e. the workspace was already
|
|
21
|
+
opted in via cockpit's Setup Wizard or a manual `initProjection`.
|
|
22
|
+
Either marker counts as explicit user consent for this workspace.
|
|
23
|
+
Workspaces with neither still bail silently — random scratch dirs
|
|
24
|
+
still don't get session events.
|
|
25
|
+
|
|
26
|
+
### Why
|
|
27
|
+
|
|
28
|
+
Cockpit-vscode 0.3.0+ ships the Setup Wizard which creates
|
|
29
|
+
`<workspace>/.dru-code/sessions-db.json` on Enable. Before this fix,
|
|
30
|
+
the hook then rejected every SessionStart in that workspace because no
|
|
31
|
+
CLAUDE.md sentinel was present — events.jsonl stayed empty and the
|
|
32
|
+
SESSIONS panel showed `0 active` forever. The `.dru-code/` file
|
|
33
|
+
already represents user consent; the gate now treats it as such.
|
|
34
|
+
|
|
35
|
+
### Test
|
|
36
|
+
|
|
37
|
+
- New `contract-1b` test in
|
|
38
|
+
`__tests__/hook/sessions-db-session-start.test.mjs` plants a
|
|
39
|
+
`.dru-code/sessions-db.json` in a workspace with NO CLAUDE.md
|
|
40
|
+
sentinel and verifies the hook records a session_seen event.
|
|
41
|
+
Existing `contract-1` still passes (workspace with neither marker
|
|
42
|
+
still rejects).
|
|
43
|
+
- Full suite: 446 tests, 0 fail.
|
|
44
|
+
|
|
45
|
+
### No public API change
|
|
46
|
+
|
|
47
|
+
Same exported surface as 0.1.3. The gate widening is additive;
|
|
48
|
+
consumers that previously passed still pass. Cockpit pin
|
|
49
|
+
`>=0.1.0 <0.2.0` picks up 0.1.4 automatically on `npm install`.
|
|
50
|
+
|
|
51
|
+
## [0.1.3] — 2026-05-15
|
|
52
|
+
|
|
53
|
+
CI metadata patch. **Same source code as 0.1.1 / 0.1.2** — both prior
|
|
54
|
+
versions stayed tombstone tags because separate npm-side gates
|
|
55
|
+
rejected the publish. This release fixes the second one. Cockpit pin
|
|
56
|
+
`>=0.1.0 <0.2.0` will pick up 0.1.3.
|
|
57
|
+
|
|
58
|
+
### Fixed (CI / supply chain)
|
|
59
|
+
|
|
60
|
+
- `package.json` `repository.url` switched from
|
|
61
|
+
`git+ssh://git@gitlab.tinfant.org:8922/druumen/sessions-db.git` to
|
|
62
|
+
`git+https://github.com/druumen/sessions-db.git`. Required by npm
|
|
63
|
+
registry's provenance verification: when a package is published with
|
|
64
|
+
`--provenance` from GitHub Actions, npm rejects (HTTP 422) if the
|
|
65
|
+
signed provenance source URL doesn't match `repository.url` in the
|
|
66
|
+
shipped tarball's package.json. Defense against supply-chain attacks
|
|
67
|
+
where attestation comes from a different repo than the metadata
|
|
68
|
+
claims.
|
|
69
|
+
|
|
70
|
+
### Repo SSoT vs publish-canonical mirror
|
|
71
|
+
|
|
72
|
+
- **Source-of-truth (development)**: `gitlab.tinfant.org/druumen/sessions-db`
|
|
73
|
+
(private to tinfant org, where MRs land + CI runs first).
|
|
74
|
+
- **Publish-canonical (npm-visible)**: `github.com/druumen/sessions-db`
|
|
75
|
+
(public mirror, what `npm publish --provenance` attests to + what
|
|
76
|
+
`npm view @druumen/sessions-db | grep repository` shows consumers).
|
|
77
|
+
|
|
78
|
+
This split is now documented in `README.md` and `RELEASING.md`. Issues
|
|
79
|
+
+ MRs continue to live on GitLab; the GitHub mirror is for `npm
|
|
80
|
+
install` consumers' "view source" link + provenance attestation.
|
|
81
|
+
|
|
82
|
+
### Tombstone tag note (0.1.2)
|
|
83
|
+
|
|
84
|
+
- `v0.1.2` exists on GitLab + GitHub mirror as a permanent tag against
|
|
85
|
+
commit `d908df77` but is **NOT published to npm**. The OIDC publish
|
|
86
|
+
attempt got past the auth fix from 0.1.2's CI change (npm 11.5.x),
|
|
87
|
+
signed provenance to Sigstore log `1549510274` (immutable), but
|
|
88
|
+
failed at the registry PUT with `422 Unprocessable Entity` because
|
|
89
|
+
of the repo URL mismatch fixed in 0.1.3.
|
|
90
|
+
- Together with `v0.1.1` (logIndex 1547090299 / 1549427812), there are
|
|
91
|
+
now **3 sigstore provenance records** on the public transparency log
|
|
92
|
+
for failed-publish attempts of this package's 0.1.x series. They
|
|
93
|
+
prove the GitHub Actions runner attempted publishes and serve as
|
|
94
|
+
audit trail.
|
|
95
|
+
|
|
96
|
+
### `[ASSUMPTION]` lessons recorded (per `feedback_tag_vendor_assumptions_in_plans`)
|
|
97
|
+
|
|
98
|
+
Day-of-OIDC-bringup assumption miss #2 (after the npm-version one):
|
|
99
|
+
- "Provenance verification accepts any repository.url in package.json"
|
|
100
|
+
→ wrong. npm rejects 422 if signed provenance source doesn't match
|
|
101
|
+
metadata. This is npm's documented behavior but wasn't surfaced in
|
|
102
|
+
the original D15 design or RELEASING.md.
|
|
103
|
+
|
|
104
|
+
Will save a second feedback memory specifically for "OIDC publish
|
|
105
|
+
requires repo-URL alignment with provenance signer" so future plans
|
|
106
|
+
flag it.
|
|
107
|
+
|
|
108
|
+
## [0.1.2] — 2026-05-15
|
|
109
|
+
|
|
110
|
+
CI-only patch. **Same source code as 0.1.1** — but 0.1.1 never actually
|
|
111
|
+
landed on the npm registry; it stayed a tombstone tag because the
|
|
112
|
+
GitHub Actions OIDC publish workflow used npm 10.8.2 (default for
|
|
113
|
+
Node 20), which signs sigstore provenance fine but lacks the OIDC
|
|
114
|
+
trusted-publisher token-exchange flow that npm registry requires
|
|
115
|
+
(added in npm **11.5.1**). The publish PUT got 404 (npm-style auth
|
|
116
|
+
masking) twice in a row.
|
|
117
|
+
|
|
118
|
+
This release fixes the workflow + ships the same library code under
|
|
119
|
+
0.1.2. Cockpit pinning `>=0.1.0 <0.2.0` will pick this up
|
|
120
|
+
automatically; no manual install change needed.
|
|
121
|
+
|
|
122
|
+
### Fixed (CI / supply chain)
|
|
123
|
+
|
|
124
|
+
- `.github/workflows/publish.yml` now runs `npm install -g npm@latest`
|
|
125
|
+
before the publish step. Picks up OIDC trusted publisher support
|
|
126
|
+
(≥11.5.1) without changing the runner's Node version (constrained
|
|
127
|
+
by `engines: ">=18.0.0"` in package.json).
|
|
128
|
+
|
|
129
|
+
### Tombstone tag note (0.1.1)
|
|
130
|
+
|
|
131
|
+
- `v0.1.1` exists on GitLab + GitHub mirror as a permanent tag against
|
|
132
|
+
commit `4350814e` but is **NOT published to npm**. Two failed OIDC
|
|
133
|
+
publish attempts left two sigstore provenance records on the public
|
|
134
|
+
transparency log (`logIndex 1547090299` and `logIndex 1549427812`),
|
|
135
|
+
immutable forever — they prove the GitHub Actions runner attempted
|
|
136
|
+
to publish that tag. Consumers should ignore `v0.1.1` entirely.
|
|
137
|
+
- The 0.1.1 CHANGELOG entry is preserved below as the full record of
|
|
138
|
+
the packaging fixes that **shipped under 0.1.2**.
|
|
139
|
+
|
|
140
|
+
### `[ASSUMPTION]` lesson recorded
|
|
141
|
+
|
|
142
|
+
- Per memory `feedback_tag_vendor_assumptions_in_plans` (saved
|
|
143
|
+
2026-05-15): the assumption "npm CLI shipped with Node 20 supports
|
|
144
|
+
OIDC trusted publisher" was written as fact in the original D15
|
|
145
|
+
publish.yml. Both Codex round-2 review and the cockpit owner's
|
|
146
|
+
bootstrap step 7 missed it because both audited "trusted publisher
|
|
147
|
+
is configured" without checking "is the runner's npm version
|
|
148
|
+
capable of using it." Real-world v0.1.1 publish surfaced the gap.
|
|
149
|
+
|
|
150
|
+
## [0.1.1] — 2026-05-15
|
|
151
|
+
|
|
152
|
+
Packaging-only patch release. Fixes 3 independent bugs surfaced by the
|
|
153
|
+
first real consumer (Druumen Cockpit Phase 3 B1 integration) within
|
|
154
|
+
hours of 0.1.0 publish. **Zero runtime/library code changes** — same
|
|
155
|
+
public API surface, same test coverage. The fix is in how the package
|
|
156
|
+
is shipped, not what it does.
|
|
157
|
+
|
|
158
|
+
This is also the **first OIDC publish path test** — released via
|
|
159
|
+
GitHub Actions trusted publisher (no NPM_TOKEN_BOOTSTRAP), with
|
|
160
|
+
`--provenance` attestations. Consumers can now verify provenance via
|
|
161
|
+
`npm view @druumen/sessions-db@0.1.1 --json | jq .dist.attestations`.
|
|
162
|
+
|
|
163
|
+
### Fixed
|
|
164
|
+
|
|
165
|
+
- **Bug A — Node16 module resolution ignores top-level `types`** when
|
|
166
|
+
`exports` map is present. 0.1.0 had bare-string-form
|
|
167
|
+
`"exports": { ".": "./lib/index.mjs" }` plus a top-level
|
|
168
|
+
`"types": "./types/index.d.ts"` — the top-level was silently
|
|
169
|
+
dropped under cockpit's `moduleResolution: "Node16"`. Symptom:
|
|
170
|
+
`TS7016: Could not find a declaration file for module '@druumen/sessions-db'`.
|
|
171
|
+
Fix: conditional exports map with explicit `types` + `import` +
|
|
172
|
+
`require` + `default` per entry. The top-level `types` is kept as
|
|
173
|
+
legacy fallback for `moduleResolution: "node"` (older TypeScript).
|
|
174
|
+
|
|
175
|
+
- **Bug B — `types/index.d.ts` re-exported type aliases not values**.
|
|
176
|
+
0.1.0 had a hand-crafted `types/index.d.ts` with patterns like
|
|
177
|
+
`export type LoadProjection = typeof import('./storage.d.mts').loadProjection`
|
|
178
|
+
— these are TYPE ALIASES, not VALUE re-exports. Consumer could write
|
|
179
|
+
`import type { LoadProjection }` but not `import { loadProjection }`.
|
|
180
|
+
Symptom: `TS2305: Module '@druumen/sessions-db' has no exported
|
|
181
|
+
member 'loadProjection'`. Root cause: stale Day-2 artifact when
|
|
182
|
+
`lib/index.mjs` was a stub; never updated when Day 3 added real
|
|
183
|
+
value re-exports to `lib/index.mjs`. Fix: replace with a barrel
|
|
184
|
+
pattern that stitches `./index.d.mts` (auto-emitted value re-exports
|
|
185
|
+
mirroring lib/index.mjs) + `./types.d.mts` (auto-emitted type
|
|
186
|
+
declarations from lib/types.mjs `@typedef` block).
|
|
187
|
+
|
|
188
|
+
- **Bug C — pure ESM rejected by Node16 CJS context**. 0.1.0 was pure
|
|
189
|
+
ESM (`"type": "module"` + only `.mjs` source). Cockpit (Node16 +
|
|
190
|
+
no `"type":"module"` → CJS context) hit `TS1479: ECMAScript module
|
|
191
|
+
cannot be imported with require`. Fix: dual CJS+ESM build via
|
|
192
|
+
esbuild — `lib/index.cjs` (62 KB bundle) is generated alongside
|
|
193
|
+
`lib/index.mjs` by `npm run build:cjs`. Exports map's `require`
|
|
194
|
+
condition routes CJS consumers to the bundle, `import` condition
|
|
195
|
+
keeps ESM consumers on the per-file structure. The bundle is
|
|
196
|
+
regenerated at `prepublishOnly` time so it always matches the
|
|
197
|
+
current `lib/index.mjs` exports.
|
|
198
|
+
|
|
199
|
+
### Added
|
|
200
|
+
|
|
201
|
+
- **Regression guards** so Bug A / B / C class issues surface at
|
|
202
|
+
publish time, not consumer integration time:
|
|
203
|
+
- `__tests__/pack-install-smoke/pack-install-smoke.test.mjs` —
|
|
204
|
+
end-to-end packaged-consumer smoke. `npm pack`s the source,
|
|
205
|
+
installs the tarball into a temp consumer dir, then exercises
|
|
206
|
+
the actual `package.json` exports map via 3 consumer styles:
|
|
207
|
+
(a) CJS `require('@druumen/sessions-db')`, (b) ESM
|
|
208
|
+
`import('@druumen/sessions-db')`, (c) TypeScript
|
|
209
|
+
`moduleResolution: "Node16"` with both type and value imports.
|
|
210
|
+
This is the canonical "consumer's POV" test — it would have
|
|
211
|
+
caught all 3 of 0.1.0's Bug A / B / C at publish time. The other
|
|
212
|
+
smokes complement it but bypass the exports map.
|
|
213
|
+
- `__tests__/cjs-smoke/cjs-smoke.test.mjs` — runtime CJS smoke
|
|
214
|
+
against `lib/index.cjs` directly. Asserts 35+ functions + 7+
|
|
215
|
+
constants are callable.
|
|
216
|
+
- `__tests__/types-smoke/cockpit-import.ts` — added VALUE imports
|
|
217
|
+
block (was type-imports-only).
|
|
218
|
+
- `__tests__/types-smoke/tsconfig.json` switched from
|
|
219
|
+
`moduleResolution: "Bundler"` to `"Node16"`.
|
|
220
|
+
- **CI build-freshness gate** — both GitLab `test-linux` and GitHub
|
|
221
|
+
Actions `Windows CI` now run `npm run build` followed by
|
|
222
|
+
`git diff --exit-code lib/index.cjs types/`. If a contributor
|
|
223
|
+
edits `lib/*.mjs` (changing exported signatures) but forgets to
|
|
224
|
+
rerun the build before commit, CI fails fast at PR time instead
|
|
225
|
+
of shipping a stale bundle to npm.
|
|
226
|
+
|
|
227
|
+
### Build
|
|
228
|
+
|
|
229
|
+
- **`esbuild` ^0.25.x** added as a devDependency (single dep, no
|
|
230
|
+
runtime cost — bundle output has zero deps). `npm run build:cjs`
|
|
231
|
+
produces `lib/index.cjs` from `lib/index.mjs`. `npm run build`
|
|
232
|
+
runs both `build:types` (tsc) and `build:cjs` (esbuild).
|
|
233
|
+
`prepublishOnly` runs `npm run build` so the tarball always
|
|
234
|
+
contains the freshly-bundled CJS + freshly-emitted .d.mts.
|
|
235
|
+
|
|
236
|
+
- `package.json` `"main"` switched to `./lib/index.cjs` (CJS entry
|
|
237
|
+
for legacy tooling). `"module"` field added pointing to
|
|
238
|
+
`./lib/index.mjs` (legacy bundler hint, e.g. webpack 4).
|
|
239
|
+
|
|
240
|
+
### Tarball delta vs 0.1.0
|
|
241
|
+
|
|
242
|
+
- 50 → 51 files (+1: `lib/index.cjs`)
|
|
243
|
+
- 108.6 KB → 123.6 KB (+15 KB, all CJS bundle)
|
|
244
|
+
- 371.2 KB → 432.8 KB unpacked (still well under target)
|
|
245
|
+
|
|
246
|
+
### Codex round + lessons
|
|
247
|
+
|
|
248
|
+
- Codex adversarial review applied (agentId: see commit message of
|
|
249
|
+
the round-2 fix commit).
|
|
250
|
+
- Per `feedback_tag_vendor_assumptions_in_plans` saved 2026-05-15:
|
|
251
|
+
the assumption "ESM-only is fine for npm publishing" should have
|
|
252
|
+
been tagged `[ASSUMPTION]` in the original D-path plan, not
|
|
253
|
+
written as fact. Real-world consumer (cockpit Node16 CJS) surfaced
|
|
254
|
+
the gap. Documented for future plan-drafting discipline.
|
|
255
|
+
|
|
8
256
|
## [0.1.0] — 2026-05-15
|
|
9
257
|
|
|
10
258
|
First public release. Extracted from the Druumen monorepo as a
|
|
@@ -41,7 +289,7 @@ dependencies.
|
|
|
41
289
|
never walks to `/` on a slow networked mount.
|
|
42
290
|
- **TypeScript types**: hand-curated `types/index.d.ts` re-export hub
|
|
43
291
|
plus auto-emitted `.d.mts` siblings via `tsc --emitDeclarationOnly`
|
|
44
|
-
|
|
292
|
+
driven by JSDoc on the source `.mjs` files. Cockpit and other TS consumers
|
|
45
293
|
can `import type { KnownSession, Projection } from '@druumen/sessions-db'`.
|
|
46
294
|
- **Cross-platform**: macOS / Linux / Windows all supported and
|
|
47
295
|
CI-gated. Linux runs on GitLab `test-linux` (Node 20). Windows runs
|
|
@@ -88,6 +336,13 @@ dependencies.
|
|
|
88
336
|
using a one-time `NPM_TOKEN_BOOTSTRAP` Granular Access Token (48h
|
|
89
337
|
expiry, `@druumen` scope, masked + protected + environment-scoped
|
|
90
338
|
variable, revoked immediately after publish).
|
|
339
|
+
- **v0.1.0 published WITHOUT provenance attestations** — intentional.
|
|
340
|
+
The bootstrap path runs from GitLab CI which has no GitHub-Actions-
|
|
341
|
+
style OIDC token issuer for npm; the npm registry only accepts
|
|
342
|
+
provenance from a recognized OIDC publisher (currently GitHub Actions
|
|
343
|
+
and GitLab.com SaaS). `npm view @druumen/sessions-db@0.1.0 --json | jq
|
|
344
|
+
.dist.attestations` returns `{}`. This is a one-time gap covering
|
|
345
|
+
only the bootstrap release; v0.1.1 onwards have full provenance.
|
|
91
346
|
- **v0.1.1 onwards** publish from GitHub Actions
|
|
92
347
|
(`.github/workflows/publish.yml`) via npm **OIDC trusted publishing**
|
|
93
348
|
— no long-lived secrets, short-lived OIDC tokens validated by npm
|
|
@@ -247,3 +502,44 @@ contract.
|
|
|
247
502
|
releases, uses `id-token: write` + `--provenance` flag for npm
|
|
248
503
|
attestations. Inactive until trusted publisher configured on npm
|
|
249
504
|
web (Bootstrap step 7).
|
|
505
|
+
|
|
506
|
+
### Day 8 — 2026-05-15 (v0.1.0 published — 3 lessons learned)
|
|
507
|
+
|
|
508
|
+
- **Published**: `@druumen/sessions-db@0.1.0` live on npm registry at
|
|
509
|
+
2026-05-15T08:22:56Z, Apache-2.0, `dist.shasum a70980a7…`. Pipeline
|
|
510
|
+
#429 (post-release-prep merge `645a8a4e`): `test-linux` 9.4s +
|
|
511
|
+
`mirror-to-github` 5.2s + `publish-npm` 12.6s. Trusted publisher
|
|
512
|
+
configured on npm web (org=druumen, repo=sessions-db, workflow=
|
|
513
|
+
publish.yml, env=npm-publish) immediately after bootstrap revoke,
|
|
514
|
+
arming OIDC path for 0.1.1+.
|
|
515
|
+
|
|
516
|
+
- **Lesson 1 (Δ35 — protected `v*` tag gap)**: First v0.1.0 tag
|
|
517
|
+
pipeline `mirror-to-github` failed because `GITHUB_MIRROR_TOKEN`
|
|
518
|
+
(protected variable) wasn't accessible from `v*` tag pipelines —
|
|
519
|
+
only `master` + `fix/*` were in the protected refs list. Fixed by
|
|
520
|
+
adding `v*` to GitLab Protected Tags (Maintainers can create).
|
|
521
|
+
RELEASING.md pre-flight now explicitly lists the protected-refs
|
|
522
|
+
audit including `v*`.
|
|
523
|
+
|
|
524
|
+
- **Lesson 2 (Δ36 — npm Granular "Bypass 2FA" checkbox)**: 5
|
|
525
|
+
consecutive `EOTP npm error code EOTP` failures during initial
|
|
526
|
+
publish attempts. Root cause discovered: npm removed Classic
|
|
527
|
+
Automation tokens in November 2025; only Granular Access Tokens
|
|
528
|
+
are now supported, and Granular tokens require an OTP at publish
|
|
529
|
+
time **even when the account is in `auth-only` 2FA mode**, unless
|
|
530
|
+
the explicit "Bypass two-factor authentication (2FA)" checkbox is
|
|
531
|
+
ticked at token creation. Token regenerated with the checkbox →
|
|
532
|
+
immediate publish success. RELEASING.md Step 1 now flags this as
|
|
533
|
+
a `MUST be checked` item with prominent ⚠️ marker.
|
|
534
|
+
|
|
535
|
+
- **Lesson 3 (Δ37 — v0.1.0 has no provenance)**: Documented in the
|
|
536
|
+
Supply chain section above. Bootstrap path runs from GitLab CI
|
|
537
|
+
which lacks GitHub-Actions-style npm OIDC integration; v0.1.0 ships
|
|
538
|
+
without `dist.attestations`. Intentional and one-time — v0.1.1+ via
|
|
539
|
+
OIDC restores full provenance.
|
|
540
|
+
|
|
541
|
+
- **Cockpit Phase 3 unblocked**: B1-B14 implementation begins
|
|
542
|
+
immediately on cockpit side. `npm install @druumen/sessions-db`
|
|
543
|
+
works for marketplace prep. Expect 1-2 minor patch releases
|
|
544
|
+
(0.1.1 / 0.1.2) shaking out integration corner cases — these will
|
|
545
|
+
be the first real exercise of the OIDC publish path.
|
package/README.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Cross-session traceability for [Claude Code](https://claude.com/claude-code).
|
|
4
4
|
|
|
5
|
+
> **Repository note**: development happens at
|
|
6
|
+
> `gitlab.tinfant.org/druumen/sessions-db` (private to tinfant org —
|
|
7
|
+
> file MRs there). The `github.com/druumen/sessions-db` mirror is
|
|
8
|
+
> public and is the source-of-truth for npm provenance attestations
|
|
9
|
+
> + the consumer-facing "view source" link from
|
|
10
|
+
> <https://www.npmjs.com/package/@druumen/sessions-db>. Both are kept
|
|
11
|
+
> in sync via GitLab CI's `mirror-to-github` job.
|
|
12
|
+
|
|
5
13
|
## What it does
|
|
6
14
|
|
|
7
15
|
Records every Claude Code session start (cwd, branch, transcript file,
|
|
@@ -120,6 +128,26 @@ The hook is bootstrap-safe by design:
|
|
|
120
128
|
- Errors are logged to stderr (visible in Claude Code's session log)
|
|
121
129
|
but never surfaced as user-facing failures.
|
|
122
130
|
|
|
131
|
+
#### Subpath imports `./cli` and `./hook` are ESM-only
|
|
132
|
+
|
|
133
|
+
The `@druumen/sessions-db/cli` and `@druumen/sessions-db/hook` exports
|
|
134
|
+
resolve to `.mjs` entry points and are intended to be **executed as
|
|
135
|
+
processes** (via the `bin` field's shim, or invoked directly with
|
|
136
|
+
`node <path>`). They are NOT intended for programmatic `require()` /
|
|
137
|
+
`import` from a consumer's runtime code.
|
|
138
|
+
|
|
139
|
+
If you need to run the CLI from your code, spawn it as a child process:
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
// CJS or ESM consumer — spawn the CLI as a process
|
|
143
|
+
import { spawnSync } from 'node:child_process';
|
|
144
|
+
spawnSync('npx', ['sessions-db', 'find', '...'], { stdio: 'inherit' });
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The main library entry (`@druumen/sessions-db`) IS dual-published as
|
|
148
|
+
both CJS and ESM and works from either context. Only the bin-style
|
|
149
|
+
subpaths are ESM-only.
|
|
150
|
+
|
|
123
151
|
## Path resolution
|
|
124
152
|
|
|
125
153
|
When you don't pass an explicit `rootPath`, sessions-db walks a 5-priority
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
* from a hook that is purely observational.
|
|
10
10
|
*
|
|
11
11
|
* Six-item safety contract (every test below cross-references one item):
|
|
12
|
-
* 1. cwd-gate: bail on any cwd
|
|
13
|
-
*
|
|
12
|
+
* 1. cwd-gate: bail on any cwd that is neither a Druumen Workspace
|
|
13
|
+
* (CLAUDE.md sentinel) nor an opted-in workspace (existing
|
|
14
|
+
* `.dru-code/sessions-db.json` or `tickets/_logs/sessions-db.json`
|
|
15
|
+
* under cwd or any ancestor). No event written when both rejected.
|
|
14
16
|
* 2. < 2 second budget: bootstrap's setTimeout(2000ms).unref() always wins.
|
|
15
17
|
* Each sub-probe respects a single global deadline derived from
|
|
16
18
|
* `gitContext({ totalBudgetMs })` — six probes can never sum past the
|
|
@@ -76,9 +78,13 @@ async function main() {
|
|
|
76
78
|
process.env.CLAUDE_PROJECT_DIR ||
|
|
77
79
|
process.cwd();
|
|
78
80
|
|
|
79
|
-
// (3) cwd-gate.
|
|
80
|
-
//
|
|
81
|
-
//
|
|
81
|
+
// (3) cwd-gate. Accept the cwd when EITHER of:
|
|
82
|
+
// - a CLAUDE.md "Druumen Workspace" sentinel exists at cwd or ancestor
|
|
83
|
+
// (druumen-monorepo opt-in), OR
|
|
84
|
+
// - a `.dru-code/sessions-db.json` or `tickets/_logs/sessions-db.json`
|
|
85
|
+
// already exists under cwd or ancestor (cockpit Setup Wizard or
|
|
86
|
+
// prior manual init already opted this workspace in).
|
|
87
|
+
// Any other repo bails silently.
|
|
82
88
|
if (!isDruumenWorkspace(cwd)) {
|
|
83
89
|
process.exit(0);
|
|
84
90
|
}
|
|
@@ -262,27 +268,59 @@ function readStdinJson({ timeoutMs }) {
|
|
|
262
268
|
}
|
|
263
269
|
|
|
264
270
|
/**
|
|
265
|
-
*
|
|
266
|
-
* "Druumen Workspace" sentinel. Bounded to 12 ancestors so a runaway loop
|
|
267
|
-
* (e.g. weird filesystem mount) cannot stall us.
|
|
271
|
+
* Decide whether the hook is allowed to record events for `cwd`.
|
|
268
272
|
*
|
|
269
|
-
*
|
|
273
|
+
* Two acceptance fast-paths, either of which is sufficient:
|
|
274
|
+
*
|
|
275
|
+
* 1. **Druumen Workspace sentinel** — a `CLAUDE.md` at `cwd` or any
|
|
276
|
+
* ancestor whose body contains the literal string "Druumen Workspace".
|
|
277
|
+
* Original 0.1.x gate; how the Druumen monorepo opts in.
|
|
278
|
+
*
|
|
279
|
+
* 2. **Pre-initialized sessions-db storage** — `.dru-code/sessions-db.json`
|
|
280
|
+
* or `tickets/_logs/sessions-db.json` already exists at `cwd` or any
|
|
281
|
+
* ancestor. The cockpit Setup Wizard creates this file when the user
|
|
282
|
+
* explicitly enables sessions tracking for a workspace; an external
|
|
283
|
+
* project that has never opted in will not have either marker.
|
|
284
|
+
*
|
|
285
|
+
* Either marker is treated as user consent for this workspace. The walk
|
|
286
|
+
* is bounded to 12 ancestors so a runaway loop (e.g. weird FS mount)
|
|
287
|
+
* cannot stall us; the loop terminates early as soon as ANY marker is
|
|
288
|
+
* found at the current level.
|
|
289
|
+
*
|
|
290
|
+
* The function name is kept (`isDruumenWorkspace`) for git history clarity
|
|
291
|
+
* even though the semantic has broadened to "authorized workspace". Both
|
|
292
|
+
* acceptance criteria are checked at each ancestor before walking up
|
|
293
|
+
* (cheap stat-only probes for the storage paths).
|
|
294
|
+
*
|
|
295
|
+
* Returns true on the first hit, false after 12 ancestors / filesystem
|
|
296
|
+
* root / read errors with no marker found.
|
|
270
297
|
*/
|
|
271
298
|
function isDruumenWorkspace(cwd) {
|
|
272
299
|
if (typeof cwd !== 'string' || cwd.length === 0) return false;
|
|
273
300
|
let dir = cwd;
|
|
274
301
|
for (let i = 0; i < 12; i++) {
|
|
275
|
-
|
|
276
|
-
|
|
302
|
+
// Fast-path 1: CLAUDE.md sentinel
|
|
303
|
+
const claudeMd = join(dir, 'CLAUDE.md');
|
|
304
|
+
if (existsSync(claudeMd)) {
|
|
277
305
|
try {
|
|
278
306
|
// We only need the first ~8KB to find the sentinel; CLAUDE.md is
|
|
279
307
|
// typically short, so reading the whole file is fine.
|
|
280
|
-
const body = readFileSync(
|
|
308
|
+
const body = readFileSync(claudeMd, 'utf8');
|
|
281
309
|
if (body.includes('Druumen Workspace')) return true;
|
|
282
310
|
} catch {
|
|
283
311
|
// unreadable — keep walking up just in case there's a higher one.
|
|
284
312
|
}
|
|
285
313
|
}
|
|
314
|
+
// Fast-path 2: pre-initialized sessions-db storage. Stat-only — we
|
|
315
|
+
// don't read these files here, just check existence. Either convention
|
|
316
|
+
// (cockpit-marketplace `.dru-code/` or druumen-monorepo `tickets/_logs/`)
|
|
317
|
+
// counts as opt-in.
|
|
318
|
+
if (
|
|
319
|
+
existsSync(join(dir, '.dru-code', 'sessions-db.json')) ||
|
|
320
|
+
existsSync(join(dir, 'tickets', '_logs', 'sessions-db.json'))
|
|
321
|
+
) {
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
286
324
|
const parent = dirname(dir);
|
|
287
325
|
if (parent === dir) return false;
|
|
288
326
|
dir = parent;
|