fix(cli): vault import with --vault no longer demands a default profile

`vault import` resolved the active fingerprint unconditionally, even though
an explicit --vault target never uses it. On a machine with no ~/.seedpass
that made the command fail with "no profile selected" -- and it is why the
ts-parity workflow has been red for 33 straight runs while every local run
passed: the developer machine has a real ~/.seedpass, so the environment
coupling was invisible exactly where it mattered. The profile lookup is now
lazy, used only for the default-vault path.

The deeper fix is in the test harness: cli.test.ts now sets a hermetic
SEEDPASS_APP_DIR, so any code path that touches the default profile
directory fails locally the way it fails in CI instead of borrowing the
developer's real vault state. Verified as a negative control: with the
hermetic env and the old code, the import test reproduces CI's exact
"Unexpected end of JSON input" locally; with the fix, all 158 CLI tests
pass.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Tkj4rzKBA3uhfgk7kqjsb8
This commit is contained in:
thePR0M3TH3ANandClaude Opus 5 committed 2026-08-18 14:26:16 -04:00
1 parent ecff57e8c6
commit cf433e3a73
2 files changed
+13 -2

No files matched your search

+7 -2
View File
@@ -1578,8 +1578,13 @@ export function buildProgram(io: ProgramIo = defaultIo): Command {
}
const targetMnemonic = mnemonic ?? (await resolveMnemonic(app, opts));
const fp = await currentFingerprint(app, opts);
const vaultPath = opts.vault ?? join(app.profileDir(fp), INDEX_FILENAME);
// Resolve the profile only when the target actually depends on it: an
// explicit --vault path needs no profile, and demanding one anyway made
// `vault import --vault x` fail on machines with no ~/.seedpass — an
// environment coupling that local runs masked for months because this
// machine has one. (The CI-only parity failure was exactly this.)
const vaultPath =
opts.vault ?? join(app.profileDir(await currentFingerprint(app, opts)), INDEX_FILENAME);
if (existsSync(vaultPath) && !o.yes) {
const current = await openVault(vaultPath, targetMnemonic);
if (Object.keys(current.index.entries).length > 0) {
+6
View File
@@ -50,6 +50,12 @@ async function run(...argv: string[]): Promise<RunResult> {
beforeAll(async () => {
process.env["SEEDPASS_MNEMONIC"] = MNEMONIC;
// Hermetic app dir: without this, any code path that touches the DEFAULT
// profile directory silently depends on the developer's real ~/.seedpass —
// which is exactly how a "vault import --vault x needs a default profile"
// bug passed locally for months while failing in CI, where no ~/.seedpass
// exists. Tests must fail the way CI fails.
process.env["SEEDPASS_APP_DIR"] = await mkdtemp(join(tmpdir(), "seedpass-cli-appdir-"));
const dir = await mkdtemp(join(tmpdir(), "seedpass-cli-"));
vaultPath = join(dir, "vault.enc");
const key = deriveIndexKeyBytes(MNEMONIC);