diff --git a/js/packages/cli/src/program.ts b/js/packages/cli/src/program.ts index 587084e5..fe4059cc 100644 --- a/js/packages/cli/src/program.ts +++ b/js/packages/cli/src/program.ts @@ -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) { diff --git a/js/packages/cli/test/cli.test.ts b/js/packages/cli/test/cli.test.ts index 3bf605f4..656218c4 100644 --- a/js/packages/cli/test/cli.test.ts +++ b/js/packages/cli/test/cli.test.ts @@ -50,6 +50,12 @@ async function run(...argv: string[]): Promise { 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);