From 52ae5789825e5931d1166149e28cb3ad8b49a430 Mon Sep 17 00:00:00 2001 From: Yichen Jiang Date: Thu, 30 Jul 2026 17:09:29 +0800 Subject: [PATCH] test(tui): pin the personal overlay to the environment layers the CLI loads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The personal-config smoke asserted that `$DSH_HOME/.env` feeds a `!!js` expression in the personal `config.yaml` — the hoist this branch removed so `credentials-local` can own that document and keep stored keys rotatable. Seed both layers instead and let one expression separate them: the welcome prefers the personal variable, so it can only render the invoking directory's value while the harness home's `.env` stays out of `process.env`. The negative that made the removal worth doing is now asserted in the assembled application, not just in the provider's unit tests. --- .../tui-agent/tests/tui-keyless-smoke.e2e.ts | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/examples/tui-agent/tests/tui-keyless-smoke.e2e.ts b/examples/tui-agent/tests/tui-keyless-smoke.e2e.ts index cd5f83e4e3..094d8549c0 100644 --- a/examples/tui-agent/tests/tui-keyless-smoke.e2e.ts +++ b/examples/tui-agent/tests/tui-keyless-smoke.e2e.ts @@ -355,18 +355,23 @@ describe('dsh CLI keyless smoke (apps/cli through the same PTY)', () => { expect(output).toContain('\u001B[?2004l') }, LOADER_SMOKE_TEST_TIMEOUT_MS) - it('applies the personal overlay: config.yaml patches the tree and .env feeds its !!js', async () => { - // The whole personal-config chain in one boot: the personal .env supplies - // the variable, config.yaml patches the tui-agent entry with a `!!js` - // reference to it, and the banner renders the patched welcome verbatim. + it('applies the personal overlay: config.yaml patches the tree, the invoking directory\'s .env feeds its !!js, and the home .env stays out of the environment', async () => { + // The whole personal-config chain in one boot, plus the environment layer + // it deliberately excludes. The single `!!js` expression prefers the + // personal variable, so the patched welcome can only render the project + // value when the harness home's .env — the credential store of + // `dsh-credentials-local` — is NOT hoisted into `process.env`; hoisting it + // would make every stored key read as a read-only launch override on the + // next run and hand it to every subprocess the agent starts. const output = await smoke({ label: 'dsh personal overlay', tempDirPrefix: 'dsh-personal-overlay-', binScript: dshBinScript, configArgs: [], prepare: seedWorkspace({ + workspace: { '.env': 'DSH_PROJECT_WELCOME=PROJECT OVERLAY READY.\n' }, personal: { - '.env': 'DSH_PERSONAL_WELCOME=PERSONAL OVERLAY READY.\n', + '.env': 'DSH_PERSONAL_WELCOME=HOME ENV LEAKED.\n', 'config.yaml': [ '- id: tui-agent', " name: '@deepseek-ai/dsh-tui-demo'", @@ -374,14 +379,15 @@ describe('dsh CLI keyless smoke (apps/cli through the same PTY)', () => { ' provider: deepseek', ' model: deepseek-v4-flash', ' workspaceContext: false', - ' welcome: !!js process.env.DSH_PERSONAL_WELCOME', + ' welcome: !!js process.env.DSH_PERSONAL_WELCOME ?? process.env.DSH_PROJECT_WELCOME', '', ].join('\n'), }, }), - actions: [{ waitFor: 'PERSONAL OVERLAY READY.', send: '/exit\r' }], + actions: [{ waitFor: 'PROJECT OVERLAY READY.', send: '/exit\r' }], }) - expect(output).toContain('PERSONAL OVERLAY READY.') + expect(output).toContain('PROJECT OVERLAY READY.') + expect(output).not.toContain('HOME ENV LEAKED.') expect(output).toContain('\u001B[?2004l') }, LOADER_SMOKE_TEST_TIMEOUT_MS)