Skip to content

fix(cli): every settings file follows HYPERFRAMES_CONFIG_DIR - #7229

Merged
miguel-heygen merged 7 commits into
mainfrom
fix/cli-config-dir
Nov 5, 2026
Merged

miguel-heygen merged 7 commits into
mainfrom
fix/cli-config-dir

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

What

HYPERFRAMES_CONFIG_DIR now moves every settings file the CLI writes, not only the credentials. Before, these used ~/.hyperframes whatever it was set to:

  • telemetry settings and install state (config.json, install-state.json)
  • the update checker's log and its pending-install record
  • project links (projects.json)
  • the record of running CLIs (running/)
  • Cloud Run's state file
  • media-use's read of the shared config.json

A CLI that cannot write its running record no longer lets an update be scheduled from it.

Why

Under an agent sandbox that only allows writes inside the workspace, ~/.hyperframes is refused. Setting HYPERFRAMES_CONFIG_DIR to a workspace folder moved the credentials but nothing else, so each of these still hit the refused folder.

The running record is how the background installer waits for every CLI to exit before replacing the package. registerRunningCli swallowed a failed write, so a CLI that could not register was invisible to an installer it had scheduled itself.

Related work

#7224 does the same for the cache (HYPERFRAMES_CACHE_DIR): previews, registry manifests, the search model, the catalog and catch-up records. Settings stay out of the cache on purpose, since a cache may be deleted.

How

hyperframesConfigDir(...parts) (utils/configDir.ts) is HYPERFRAMES_CONFIG_DIR, else ~/.hyperframes: the rule auth/paths.ts already used, which now calls it too. Each writer builds its path from it, and its module constants became functions, so the variable is read on each call. The exported CONFIG_PATH and STATE_PATH stay computed at import: tests across the CLI pin them, and the CLI never changes the variable after start.

A settings file is opened through seededConfigFile(name). When HYPERFRAMES_CONFIG_DIR is set and its copy of the file does not exist yet but ~/.hyperframes has one, it is copied over first. So someone who already set the variable for credentials keeps their telemetryEnabled: false, install id and update state, instead of the CLI minting a fresh config with telemetry on. This covers config.json, install-state.json, projects.json and Cloud Run's state. media-use reads ~/.hyperframes/config.json until the new folder has its own copy. media-use/lib/telemetry.mjs reads the variable itself, as its credentials lookup already did: it ships as plain .mjs and cannot import the CLI's modules. Its standalone copy in skills/media-use/scripts/lib/telemetry.mjs gets the same line, as the copy-parity test requires.

registerRunningCli still never throws, since it runs at the start of every command, but it returns whether it registered. scheduleBackgroundInstall schedules nothing from a process that did not, and notes why in auto-update.log when that can be written.

Test plan

  • Unit tests added/updated:
    • fails without the fix: configDir.test.ts "keeps every config file under HYPERFRAMES_CONFIG_DIR when it is set" (fails with the old telemetry/config.ts, projectLink.ts or runningCli.ts put back).
    • fails without the fix: configDir.test.ts "starts a new settings folder from ~/.hyperframes, so an opt-out and the install id move with it": ~/.hyperframes/config.json says telemetryEnabled: false, the variable points at an empty folder, and the config read there keeps the opt-out and the id. It fails with the seeding taken out.
    • fails without the fix: "an opt-out in ~/.hyperframes holds until HYPERFRAMES_CONFIG_DIR has its own config" in both media-use telemetry.test.mjs files.
    • runningCli.test.ts: registering into a folder that cannot be made returns false and does not throw.
    • autoUpdate.test.ts: an unregistered process schedules nothing and spawns no installer. It fails with the gate removed.
    • media-use/lib/telemetry.test.mjs: the shared config is read from HYPERFRAMES_CONFIG_DIR. It fails with the old file.
    • skills/media-use/scripts/lib/telemetry.test.mjs: the same for the skill copy, which now also clears HYPERFRAMES_CONFIG_DIR before its other tests. It fails with the old copy.
    • The touched files' tests (117, plus 25 in the two media-use telemetry files) pass three runs in a row.
  • Manual testing performed: not run on Windows; the paths are covered by the tests above. Cloud Run's state path has no direct test: its helper is not exported, and it is one call to hyperframesConfigDir().
  • Documentation updated (if applicable): the HYPERFRAMES_CONFIG_DIR rows in docs/guides/authentication.mdx and docs/packages/cli.mdx list what it moves.
  • Comments follow CONTRIBUTING.md "Comments": one doc line on registeredAsRunning; the comments that named CONFIG_DIR name configDir(), and the install-state comment drops its history.

Not handled: a CLI from before this change that is still running registered under ~/.hyperframes/running, and an installer scheduled by this version for someone who sets the variable waits on the new folder. That needs the variable set and an upgrade landing mid-run.

Size

About 60 lines of source across seven files, about 80 of tests and two doc rows. One fix: the settings variable covers every settings writer.

@mintlify

mintlify Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
hyperframes 🟢 Ready View Preview Nov 5, 2026, 10:22 AM

💡 Tip: Enable Automations to automatically generate PRs for you.

@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 110 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (8)

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes at 0225e3b2.

1. Blocker: a user who opted out of telemetry and already sets HYPERFRAMES_CONFIG_DIR gets telemetry turned back on.

  • Until now, that variable only moved credentials. Anyone who uses it for credentials (CI, agent sandboxes, shared machines) keeps their telemetryEnabled: false in ~/.hyperframes/config.json.
  • After this PR, configFile() points at <HYPERFRAMES_CONFIG_DIR>/config.json. On first run that file is missing, so mintConfig() writes DEFAULT_CONFIG, which has telemetryEnabled: true, plus a new anonymousId.
  • install-state.json moves too, so predecessorFound is false and the old install's breaker and bucket seed are lost.

I reproduced it at this head with a throwaway test:

  • ~/.hyperframes/config.json set to {"telemetryEnabled": false}
  • HYPERFRAMES_CONFIG_DIR set to an empty folder
  • readConfig().telemetryEnabled then returns true ("expected true to be false").

A fix: when <HYPERFRAMES_CONFIG_DIR>/config.json does not exist but ~/.hyperframes/config.json does, seed the new file from the old one, or at least carry over telemetryEnabled: false. The same applies to the update settings. Please add a test for it.

2. Blocker (lane rule): the body has no fails without the fix: line that names a test. The test plan says "It fails with the old telemetry/config.ts…" in several bullets, but nothing names a test on a fails without the fix: line. Please add one, for example:

fails without the fix: "keeps every config file under HYPERFRAMES_CONFIG_DIR when it is set" (configDir.test.ts)

What I checked:

  • The touched tests pass at this head: configDir, runningCli, autoUpdate (both files), auth/paths, telemetry/config and projectLink pass 102/102, and the two node telemetry.test.mjs files pass 23/23.
  • The registeredAsRunning gate is sound. A CLI that isn't on the running record can't schedule an installer that would replace it mid-run, and registerRunningCli still never throws.

Non-blocking nits:

  • CONFIG_PATH and STATE_PATH are still computed once at import. telemetry, doctor and dailyNotice read them, so a process that changes the variable after import gets stale paths. It's harmless for the CLI, but it goes against "read on each call".
  • There's an upgrade-window edge case for users who set the variable. A CLI from before this PR, still running, registered in ~/.hyperframes/running. An installer scheduled by the new version waits only on <HYPERFRAMES_CONFIG_DIR>/running.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at fd2f828a. Both blockers from my change request are resolved.

1. The telemetry opt-out now carries over.

  • seededConfigFile(name) copies ~/.hyperframes/<name> into HYPERFRAMES_CONFIG_DIR the first time, with COPYFILE_EXCL, so two CLIs starting together can't overwrite each other's copy. It covers config.json, install-state.json, projects.json and the Cloud Run state.
  • It does nothing when the variable is unset or points at the home folder.
  • An unreadable home folder falls through to a fresh file, as before.
  • The standalone .mjs readers fall back to the home config.json until the moved copy exists, so an opt-out holds for them too.

2. The fails without the fix: lines are in the body and name tests.

Verified at this head:

  • The touched tests pass: 103/103 vitest and 25/25 node (both telemetry.test.mjs copies).
  • I took out the seeding check in configDir.ts. "starts a new settings folder from ~/.hyperframes, so an opt-out and the install id move with it" then fails, which covers the case I reproduced before.
  • I took out the home fallback in media-use/lib/telemetry.mjs. "an opt-out in ~/.hyperframes holds until HYPERFRAMES_CONFIG_DIR has its own config" then fails.
  • Compared against its merge-base, the rest of the patch matches 0225e3b2, apart from the seeding, the unused join import and the trimmed comment.

Non-blocking nits:

  • CONFIG_PATH = configFile() now copies a file at import time, because seeding runs when the path is first built. It's harmless, but it's a side effect when the module loads.
  • If someone deletes the moved config.json to reset it, the next run copies it back from ~/.hyperframes. Resetting fully means removing both. It might be worth a line in the docs.

CI: when I approved, CI, Windows render verification, Docs and CodeQL were still running and preview-regression was queued. Everything that had finished was green.

@miguel-heygen
miguel-heygen added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 216395e Nov 5, 2026
86 checks passed
@miguel-heygen
miguel-heygen deleted the fix/cli-config-dir branch November 5, 2026 11:51
@heygengenesis heygengenesis Bot mentioned this pull request Nov 5, 2026
mitanshu450 pushed a commit to mitanshu450/hyperframes-video-generation that referenced this pull request Nov 5, 2026
* chore: release v0.9.16

* chore: format v0.9.16 release notes

* chore: add heygen-com#7228 and heygen-com#7231 to v0.9.16 notes

* chore: add heygen-com#7217 heygen-com#7224 heygen-com#7225 heygen-com#7226 heygen-com#7229 heygen-com#7233 to v0.9.16 notes
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 6, 2026
…eview

Adopts or partially adopts 11 groups: multi-worker tween replay behind
the timeline stand-in (heygen-com#7231), template entries in the shared compile
stage (heygen-com#7217), HYPERFRAMES_CACHE_DIR and HYPERFRAMES_CONFIG_DIR for
every per-user cache and settings file (heygen-com#7224, heygen-com#7229; HeyGen
credentials stay under HEYGEN_CONFIG_DIR), WebKit audio clock (heygen-com#7225),
packet-based video length (heygen-com#7233), HLG tone mapping (heygen-com#7234), fetch
network cause (heygen-com#7236), .hyperframesignore in lint (heygen-com#7239), info
--media (heygen-com#7235), lint priority (heygen-com#7244), check --budget (heygen-com#7245) and the
unknown-command answer (heygen-com#7247). bun.lock gains only the lint `ignore`
edge from heygen-com#7239, already resolved. Watermark advances to 0a6746e.

Co-authored-by: Jerrai <[email protected]>
Co-authored-by: Miguel Ángel <[email protected]>
Co-authored-by: WaterrrForever <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant