Files
gbrain/package.json
T
1055e10c23 v0.26.2 fix(oauth): bun execSync env inheritance + BIGINT-as-string bug class (#593)
* feat(oauth): add coerceTimestamp helper + fix BIGINT-as-string bug class

Postgres-js with prepare:false (auto-detected on Supabase pooler / port
6543) returns BIGINT columns as strings. Two surfaces broke on this:
(1) MCP SDK's bearerAuth checks typeof === 'number' and rejected
strings — fixed in v0.26.1 only at line 303 of oauth-provider.ts;
(2) RFC 7591 §3.2.1 requires client_id_issued_at and
client_secret_expires_at to be JSON numbers in DCR responses, not
strings — latent until v0.26.2.

Adds module-private coerceTimestamp() at the SELECT-row → JS-number
boundary. Throws on non-finite (corrupt rows fail loud, not as
fake-valid expiresAt: NaN flowing into the SDK). Returns undefined for
SQL NULL — schema permits NULL on oauth_tokens.expires_at, callers
treat NULL as expired (fail-closed) at comparison sites and preserve
undefined in DCR getClient response per RFC 7591.

Refactors 5 sites:
- L112,113 (getClient) — DCR response numeric-shape compliance.
- L274 (exchangeRefreshToken) — NULL→expired fail-closed contract.
- L296,303 (verifyAccessToken) — single guard, narrowed return.

No `!` non-null assertions: all 5 sites read nullable BIGINT columns
per src/schema.sql:362,363,372. The L296/L303 cleanup also folds in
v0.26.1's inline Number(...) at L303.

* feat(auth): add gbrain auth revoke-client subcommand

Hard-deletes the matching oauth_clients row via atomic
DELETE ... RETURNING. Schema-level FK CASCADE on oauth_tokens.client_id
and oauth_codes.client_id (src/schema.sql:370,382) purges all dependent
rows in the same transaction. No manual delete of dependents needed.

Exit 1 on no-such-client (idempotent: re-running on the same id
produces the same error). Operator-friendly output: prints the client
name + cascade confirmation, no race-prone pre-delete count.

Closes the v0.26.1 process miss where test/e2e/serve-http-oauth.test.ts
afterAll already called this subcommand — silently failing because the
subcommand didn't exist. With this fix, E2E cleanup actually purges
test clients.

* test(oauth): v0.26.2 regression coverage + bun execSync env fix

Unit additions in test/oauth.test.ts:
- 5 cases pinning coerceTimestamp contract (null/undef/string/number/
  throws-on-NaN). The throws-on-NaN case is load-bearing: pre-v0.26.2
  Number(corrupt) → NaN, NaN < now is false → expired check skipped,
  fake-valid expiresAt:NaN flowed to SDK. Now fail-closed.
- NULL expires_at on oauth_tokens insert → verifyAccessToken throws
  "Token expired". Schema permits NULL; pre-v0.26.2 hand-modified rows
  could ride past validation.
- Cascade-deleted client → previously-minted token fails
  verifyAccessToken with "Invalid token" (not "expired"). Pins the
  cascade contract independently of the CLI subprocess path.

E2E additions in test/e2e/serve-http-oauth.test.ts:
- DCR /register HTTP-level response-shape test. Spawns server with
  --enable-dcr, POSTs a client manifest, asserts typeof === 'number'
  on client_id_issued_at and (when present) client_secret_expires_at
  per RFC 7591 §3.2.1. Replaces the v0.26.1 plan's internal-store-only
  test that Codex flagged as the wrong seam.
- Real CLI subprocess test for revoke-client: register → mint token →
  revoke via execSync → assert token rejected at /mcp + cascade
  invalidation visible + re-run exits 1 with "No client found".
- afterAll guards on clientId so pre-registration beforeAll failures
  surface cleanly instead of throwing on undefined during cleanup.
  Also tracks DCR-registered clients alongside the manual one.
- Server fixture: --enable-dcr added so /register is reachable.
- Health endpoint: page_count assertion loosened from > 0 to >= 0
  + typeof number — pre-v0.26.2 broke on fresh-schema E2E runs.

bun execSync env-inheritance fix (the load-bearing infrastructure
fix that unbroke v0.26.2's full-suite test):
- bun's child_process.execSync does NOT inherit env mutations done
  via process.env.X = ...; only OS-level env from before bun started.
- helpers.ts loads .env.testing and sets DATABASE_URL via process.env
  mutation, invisible to subprocesses unless env: { ...process.env }
  is passed explicitly.
- All 4 execSync calls in this file (beforeAll register-client,
  afterAll revoke-client, in-test register-client, in-test
  revoke-client x2) now pass env: { ...process.env }.
- Without this, full bun test suite OAuth E2E fails with "Set
  DATABASE_URL or GBRAIN_DATABASE_URL environment variable" even when
  isolated test/e2e/serve-http-oauth.test.ts runs pass. Pattern is
  documented inline as a reference for other E2E test fixes (see
  TODOS.md "test infra (v0.26.2 follow-up)" for the 22-test backlog).

* build: commit admin/dist + remove gitignore exclusion

CLAUDE.md (admin/ section, v0.26.0 release notes) states:
"output at admin/dist/ is committed for self-contained binaries"

But .gitignore excluded admin/dist/, so the bun --compile binary that
embeds the admin SPA via `import path from '...' with { type: 'file' }`
couldn't resolve in fresh clones. PR #577 (v0.26.1) didn't catch this
because admin tests pass when admin/dist exists locally.

Removes the .gitignore line + commits the current 220KB build:
- index.html (0.7KB)
- assets/index-{hash}.js (210KB / 65KB gzip)
- assets/index-{hash}.css (6.3KB / 1.8KB gzip)

Now `bun build --compile --outfile bin/gbrain src/cli.ts` works on a
fresh clone without a separate `cd admin && bun install && bun run
build` step in CI.

* docs: capturing test output rule + regen llms-full.txt

Adds a CLAUDE.md section "Capturing test output (NEVER pipe through
tail / head)" documenting the iron rule that bit v0.26.2's ship:

  bun test 2>&1 | tail -10  →  exit code = tail's (always 0),
                                failures truncated, ship gates fail open

The pipe form silently breaks /ship Step T1 (test failure ownership
triage) because $? after a pipe is the LAST command's exit code, and
bun prints failure details before the summary line so tail -N drops
them. v0.26.2's first ship attempt reported "3911 pass / 23 fail" but
no failure details survived, forcing a 23-minute re-run to triage.

Right pattern: redirect to a file first, then tail the file separately.

Regenerates llms-full.txt to match the new CLAUDE.md content (drift
guard at test/build-llms.test.ts enforces this).

* docs: P0 TODO for 22 pre-existing test failures unrelated to OAuth

Captures the test-infra backlog uncovered by v0.26.2's full bun test
run. None of the 22 failing cases touch the OAuth diff:

- 12 Git-to-DB Sync Pipeline cases (state-machine drift)
- 3 multi-source cascade + sync routing cases
- E2E sync-parallel, sync --skip-failed, doctor, dream, runCycle,
  claw-test fresh-install, BrainRegistry lazy init

Likely root causes for several: same bun execSync env-inheritance
pattern fixed in test/e2e/serve-http-oauth.test.ts during v0.26.2
(documented in the TODO + the inline test comment for the next
maintainer to find).

Separating from v0.26.2 keeps the OAuth ship focused on the bug
class it was scoped for. Fix-wave deserves its own PR.

* chore: bump to v0.26.2 + CHANGELOG

VERSION 0.26.0 → 0.26.2. Includes a retroactive v0.26.1 entry above
v0.26.0 because PR #577 shipped its three fixes (oauth-provider:303
Number cast, OAuth metadata interceptor, Express 5 trust proxy +
admin wildcard) without bumping VERSION/package.json/CHANGELOG —
this branch catches the changelog up to commit history.

v0.26.2 release-summary covers the OAuth string-vs-number bug class
fix (5 sites + coerceTimestamp helper), the gbrain auth revoke-client
subcommand landing as a real CLI, and the bun execSync env-inheritance
fix that unblocked full-suite E2E OAuth tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: post-ship updates for v0.26.2

- CLAUDE.md src/core/oauth-provider.ts: append v0.26.2 coerceTimestamp boundary helper note (5 call sites, NULL semantics, throw-on-NaN posture, intentionally module-private)
- CLAUDE.md src/commands/auth.ts: add v0.26.2 revoke-client subcommand with FK CASCADE cleanup
- CLAUDE.md test/oauth.test.ts: bump v0.26.2 case additions (5 coerceTimestamp + NULL-expires_at + cascade-delete contract)
- CLAUDE.md test/e2e/serve-http-oauth.test.ts: new entry covering v0.26.0 + v0.26.2 expansion (DCR HTTP-level test, CLI subprocess revoke-client test, bun execSync env-inheritance fix as reference for sibling E2Es)
- README.md: add gbrain auth revoke-client to command list
- llms-full.txt: regenerate after CLAUDE.md edits

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 11:48:34 -07:00

94 lines
3.8 KiB
JSON

{
"name": "gbrain",
"version": "0.26.2",
"description": "Postgres-native personal knowledge brain with hybrid RAG search",
"type": "module",
"main": "src/core/index.ts",
"bin": {
"gbrain": "src/cli.ts"
},
"exports": {
".": "./src/core/index.ts",
"./engine": "./src/core/engine.ts",
"./types": "./src/core/types.ts",
"./operations": "./src/core/operations.ts",
"./minions": "./src/core/minions/index.ts",
"./engine-factory": "./src/core/engine-factory.ts",
"./pglite-engine": "./src/core/pglite-engine.ts",
"./link-extraction": "./src/core/link-extraction.ts",
"./import-file": "./src/core/import-file.ts",
"./transcription": "./src/core/transcription.ts",
"./embedding": "./src/core/embedding.ts",
"./config": "./src/core/config.ts",
"./markdown": "./src/core/markdown.ts",
"./backoff": "./src/core/backoff.ts",
"./search/hybrid": "./src/core/search/hybrid.ts",
"./search/expansion": "./src/core/search/expansion.ts",
"./extract": "./src/commands/extract.ts"
},
"scripts": {
"dev": "bun run src/cli.ts",
"build": "bun build --compile --outfile bin/gbrain src/cli.ts",
"build:all": "bun build --compile --target=bun-darwin-arm64 --outfile bin/gbrain-darwin-arm64 src/cli.ts && bun build --compile --target=bun-linux-x64 --outfile bin/gbrain-linux-x64 src/cli.ts",
"build:admin": "cd admin && bun run build",
"build:schema": "bash scripts/build-schema.sh",
"build:llms": "bun run scripts/build-llms.ts",
"build:pglite-snapshot": "bun run scripts/build-pglite-snapshot.ts",
"test": "scripts/check-privacy.sh && scripts/check-jsonb-pattern.sh && scripts/check-progress-to-stdout.sh && scripts/check-no-legacy-getconnection.sh && scripts/check-trailing-newline.sh && scripts/check-wasm-embedded.sh && scripts/check-exports-count.sh && bun run typecheck && bun test --timeout=60000",
"check:wasm": "scripts/check-wasm-embedded.sh",
"check:newlines": "scripts/check-trailing-newline.sh",
"test:e2e": "bash scripts/run-e2e.sh",
"test:slow": "bash scripts/run-slow-tests.sh",
"test:profile": "bash scripts/profile-tests.sh",
"ci:local": "bash scripts/ci-local.sh",
"ci:local:diff": "bash scripts/ci-local.sh --diff",
"ci:select-e2e": "bun run scripts/select-e2e.ts",
"typecheck": "tsc --noEmit",
"check:jsonb": "scripts/check-jsonb-pattern.sh",
"check:privacy": "scripts/check-privacy.sh",
"check:progress": "scripts/check-progress-to-stdout.sh",
"check:exports-count": "scripts/check-exports-count.sh",
"postinstall": "command -v gbrain >/dev/null 2>&1 && gbrain apply-migrations --yes --non-interactive || echo '[gbrain] postinstall skipped. If installed via bun install -g github:...: run `gbrain doctor` and `gbrain apply-migrations --yes` manually. See https://github.com/garrytan/gbrain/issues/218' 1>&2",
"prepublish:clawhub": "bun run build:all",
"publish:clawhub": "clawhub package publish . --family bundle-plugin"
},
"openclaw": {
"compat": {
"pluginApi": ">=2026.4.0"
}
},
"dependencies": {
"@anthropic-ai/sdk": "^0.30.0",
"@aws-sdk/client-s3": "^3.1028.0",
"@dqbd/tiktoken": "^1.0.22",
"@electric-sql/pglite": "0.4.3",
"@modelcontextprotocol/sdk": "1.29.0",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"express": "^5.1.0",
"express-rate-limit": "^7.5.0",
"gray-matter": "^4.0.3",
"marked": "^18.0.0",
"openai": "^4.0.0",
"pgvector": "^0.2.0",
"postgres": "^3.4.0",
"tree-sitter-wasms": "0.1.13",
"web-tree-sitter": "0.22.6"
},
"devDependencies": {
"@types/bun": "latest",
"@types/cookie-parser": "^1.4.7",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6",
"bun-types": "^1.3.13",
"typescript": "^5.6.0"
},
"trustedDependencies": [
"@electric-sql/pglite"
],
"engines": {
"bun": ">=1.3.10"
},
"license": "MIT"
}