Version
LifeOS 7.40.4 (verified against 5e2f2e8)
What is broken
The install carries five independent .env parsers, and they disagree on three inputs. Two of those disagreements are defects. PULSE/checks/calendar.ts and TOOLS/YouTubeApi.ts parse each line with ^([^#=]+)=(.*)$, and JavaScript treats a carriage return as a line terminator, so . will not match one and $ will not match before one. Given a .env saved with Windows line endings, neither file returns a truncated value: both return an empty object, and every caller then reports its credential as absent. TOOLS/healthsync/store.ts splits on /\r?\n/ and is unaffected, so the same credential file works for the health-sync subsystem and fails for these two. Separately, a .env written with export FOO=bar, which is what anyone sourcing the file in a shell will have, yields a key named literally export FOO in PULSE/pulse.ts, PULSE/checks/calendar.ts and TOOLS/YouTubeApi.ts; FOO is never set. TOOLS/healthsync/store.ts strips the prefix and is again the only one that behaves.
Where (file:line)
LIFEOS/PULSE/checks/calendar.ts:22 and LIFEOS/TOOLS/YouTubeApi.ts:65 (carriage return); LIFEOS/PULSE/pulse.ts:30, LIFEOS/PULSE/checks/calendar.ts:22, LIFEOS/TOOLS/YouTubeApi.ts:65 and LIFEOS/TOOLS/PangramScore.ts:70 (export prefix). Correct behaviour for comparison: LIFEOS/TOOLS/healthsync/store.ts:149.
Repro on a clean tree
# Fake HOME so nothing touches a real install.
mkdir -p /tmp/lifeos-crlf/.claude
# Same two keys, Windows line endings.
printf 'YOUTUBE_API_KEY=abc123\r\nYOUTUBE_CHANNEL_ID=UC_test\r\n' > /tmp/lifeos-crlf/.claude/.env
HOME=/tmp/lifeos-crlf bun LIFEOS/TOOLS/YouTubeApi.ts
# Error: YOUTUBE_API_KEY not set
# Identical keys, Unix line endings.
printf 'YOUTUBE_API_KEY=abc123\nYOUTUBE_CHANNEL_ID=UC_test\n' > /tmp/lifeos-crlf/.claude/.env
HOME=/tmp/lifeos-crlf bun LIFEOS/TOOLS/YouTubeApi.ts
# prints usage — the key was accepted
# The export prefix, Unix line endings.
printf 'export YOUTUBE_API_KEY=abc123\n' > /tmp/lifeos-crlf/.claude/.env
HOME=/tmp/lifeos-crlf bun LIFEOS/TOOLS/YouTubeApi.ts
# Error: YOUTUBE_API_KEY not set
# Second file, same cause.
printf 'GOOGLE_CALENDAR_REFRESH_TOKEN=rt\r\nGMAIL_CLIENT_ID=cid\r\nGMAIL_CLIENT_SECRET=cs\r\n' > /tmp/lifeos-crlf/.claude/.env
HOME=/tmp/lifeos-crlf bun LIFEOS/PULSE/checks/calendar.ts
# GOOGLE_CALENDAR_REFRESH_TOKEN not set
printf 'GOOGLE_CALENDAR_REFRESH_TOKEN=rt\nGMAIL_CLIENT_ID=cid\nGMAIL_CLIENT_SECRET=cs\n' > /tmp/lifeos-crlf/.claude/.env
HOME=/tmp/lifeos-crlf bun LIFEOS/PULSE/checks/calendar.ts
# Calendar check failed: Token refresh failed — the keys parsed, the fake values were rejected by Google
Negative control
On unpatched 7.40.4, with YOUTUBE_API_KEY=abc123 present in ~/.claude/.env and the file saved with CRLF endings, TOOLS/YouTubeApi.ts prints Error: YOUTUBE_API_KEY not set and exits. The same file with LF endings prints the usage banner. PULSE/checks/calendar.ts reports GOOGLE_CALENDAR_REFRESH_TOKEN not set on the CRLF file and proceeds to a token refresh on the LF file. With export prefixes and LF endings, TOOLS/YouTubeApi.ts reports the key unset while the file plainly contains it.
Behaviour of each parser against the cases that separate them, each run verbatim:
| Input |
pulse.ts |
calendar.ts / YouTubeApi.ts |
healthsync/store.ts |
A=1\r\n |
{"A":"1"} |
{} |
{"A":"1"} |
export A=1\n |
{"export A":"1"} |
{"export A":"1"} |
{"A":"1"} |
1BAD=x\nA=1\n |
{"1BAD":"x","A":"1"} |
{"1BAD":"x","A":"1"} |
{"A":"1"} |
Suggested fix
The parser in TOOLS/healthsync/store.ts already handles all three rows. Promoting it to a shared module and having the other four call it removes both defects and stops the five drifting further apart. Untested as a refactor of this tree, though the same consolidation is running in a fork with the behaviour table above as its test cases, and I am happy to open a PR if that would help.
One caveat on scope: PULSE/pulse.ts loads .env before the billing guard that strips ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN, so any replacement there has to keep that ordering.
Version
LifeOS 7.40.4 (verified against
5e2f2e8)What is broken
The install carries five independent
.envparsers, and they disagree on three inputs. Two of those disagreements are defects.PULSE/checks/calendar.tsandTOOLS/YouTubeApi.tsparse each line with^([^#=]+)=(.*)$, and JavaScript treats a carriage return as a line terminator, so.will not match one and$will not match before one. Given a.envsaved with Windows line endings, neither file returns a truncated value: both return an empty object, and every caller then reports its credential as absent.TOOLS/healthsync/store.tssplits on/\r?\n/and is unaffected, so the same credential file works for the health-sync subsystem and fails for these two. Separately, a.envwritten withexport FOO=bar, which is what anyone sourcing the file in a shell will have, yields a key named literallyexport FOOinPULSE/pulse.ts,PULSE/checks/calendar.tsandTOOLS/YouTubeApi.ts;FOOis never set.TOOLS/healthsync/store.tsstrips the prefix and is again the only one that behaves.Where (file:line)
LIFEOS/PULSE/checks/calendar.ts:22andLIFEOS/TOOLS/YouTubeApi.ts:65(carriage return);LIFEOS/PULSE/pulse.ts:30,LIFEOS/PULSE/checks/calendar.ts:22,LIFEOS/TOOLS/YouTubeApi.ts:65andLIFEOS/TOOLS/PangramScore.ts:70(exportprefix). Correct behaviour for comparison:LIFEOS/TOOLS/healthsync/store.ts:149.Repro on a clean tree
Negative control
On unpatched 7.40.4, with
YOUTUBE_API_KEY=abc123present in~/.claude/.envand the file saved with CRLF endings,TOOLS/YouTubeApi.tsprintsError: YOUTUBE_API_KEY not setand exits. The same file with LF endings prints the usage banner.PULSE/checks/calendar.tsreportsGOOGLE_CALENDAR_REFRESH_TOKEN not seton the CRLF file and proceeds to a token refresh on the LF file. Withexportprefixes and LF endings,TOOLS/YouTubeApi.tsreports the key unset while the file plainly contains it.Behaviour of each parser against the cases that separate them, each run verbatim:
pulse.tscalendar.ts/YouTubeApi.tshealthsync/store.tsA=1\r\n{"A":"1"}{}{"A":"1"}export A=1\n{"export A":"1"}{"export A":"1"}{"A":"1"}1BAD=x\nA=1\n{"1BAD":"x","A":"1"}{"1BAD":"x","A":"1"}{"A":"1"}Suggested fix
The parser in
TOOLS/healthsync/store.tsalready handles all three rows. Promoting it to a shared module and having the other four call it removes both defects and stops the five drifting further apart. Untested as a refactor of this tree, though the same consolidation is running in a fork with the behaviour table above as its test cases, and I am happy to open a PR if that would help.One caveat on scope:
PULSE/pulse.tsloads.envbefore the billing guard that stripsANTHROPIC_API_KEYandANTHROPIC_AUTH_TOKEN, so any replacement there has to keep that ordering.