From b7531f4214017227c414e64ddc2ac2198794cfe9 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Wed, 26 Aug 2026 12:31:37 -0700 Subject: [PATCH] fix(cli): exit after interactive secret input --- bun.lock | 2 +- packages/sim-cli/package.json | 2 +- .../sim-cli/src/terminal/secret-input.test.ts | 15 +++++++++------ packages/sim-cli/src/terminal/secret-input.ts | 3 +-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/bun.lock b/bun.lock index b31e872b365..986f428bd86 100644 --- a/bun.lock +++ b/bun.lock @@ -610,7 +610,7 @@ }, "packages/sim-cli": { "name": "sim", - "version": "2.1.1", + "version": "2.1.2", "bin": { "sim": "dist/index.js", }, diff --git a/packages/sim-cli/package.json b/packages/sim-cli/package.json index d03b4526d5a..18991ae7eaf 100644 --- a/packages/sim-cli/package.json +++ b/packages/sim-cli/package.json @@ -1,6 +1,6 @@ { "name": "sim", - "version": "2.1.1", + "version": "2.1.2", "description": "Sim CLI - talk to the Sim API from your terminal", "type": "module", "bin": { diff --git a/packages/sim-cli/src/terminal/secret-input.test.ts b/packages/sim-cli/src/terminal/secret-input.test.ts index 0ac0a2ca3a8..293cac472ea 100644 --- a/packages/sim-cli/src/terminal/secret-input.test.ts +++ b/packages/sim-cli/src/terminal/secret-input.test.ts @@ -8,11 +8,11 @@ const ESCAPE = '\u001b' class FakeInput extends EventEmitter { isTTY = true isRaw = false - paused = true + readableFlowing: boolean | null = null readonly rawStates: boolean[] = [] isPaused(): boolean { - return this.paused + return this.readableFlowing === false } setRawMode(value: boolean): this { @@ -22,12 +22,12 @@ class FakeInput extends EventEmitter { } resume(): this { - this.paused = false + this.readableFlowing = true return this } pause(): this { - this.paused = true + this.readableFlowing = false return this } } @@ -42,9 +42,11 @@ class FakeOutput { } describe('promptSecret', () => { - it('masks input and restores the terminal before returning it', async () => { + it('masks input and pauses an initially idle terminal before returning', async () => { const input = new FakeInput() const output = new FakeOutput() + expect(input.isPaused()).toBe(false) + const result = promptSecret(input as unknown as ReadStream, output) input.emit('keypress', 'hunter2', { name: 'h' }) @@ -53,7 +55,7 @@ describe('promptSecret', () => { await expect(result).resolves.toBe('hunter2') expect(output.value).toBe('Secret value: *******\n') expect(input.rawStates).toEqual([true, false]) - expect(input.paused).toBe(true) + expect(input.isPaused()).toBe(true) }) it('handles backspace without revealing the value', async () => { @@ -88,6 +90,7 @@ describe('promptSecret', () => { await expect(result).rejects.toThrow('Secret input cancelled.') await expect(result).rejects.toBeInstanceOf(SecretInputCancelledError) expect(input.rawStates).toEqual([true, false]) + expect(input.isPaused()).toBe(true) }) it('keeps the character following a pasted escape byte', async () => { diff --git a/packages/sim-cli/src/terminal/secret-input.ts b/packages/sim-cli/src/terminal/secret-input.ts index ca6ab56964d..1951f262f10 100644 --- a/packages/sim-cli/src/terminal/secret-input.ts +++ b/packages/sim-cli/src/terminal/secret-input.ts @@ -45,7 +45,6 @@ export function promptSecret( throw new SimApiError('Interactive secret input requires a terminal. Pass --value instead.', 0) } - const wasPaused = input.isPaused() const wasRaw = input.isRaw let value = '' let settled = false @@ -59,7 +58,7 @@ export function promptSecret( const cleanup = () => { input.removeListener('keypress', onKeypress) input.setRawMode(wasRaw) - if (wasPaused) input.pause() + input.pause() } const finish = (complete: () => void) => {