1- import { createTestDeviceInventoryGateways } from '../../__tests__/test-utils/device-inventory-gateways.ts' ;
21/**
32 * #3260: `fill --text-stdin` keeps the value out of argv, so it must not come back on the
43 * response either. Sent through the real request handler to a web session, the narrowest
54 * runtime that still runs the full fill admission, dispatch and response projection.
65 */
76import { expect , test } from 'vitest' ;
7+ import fs from 'node:fs' ;
88import path from 'node:path' ;
9+ import { createTestDeviceInventoryGateways } from '../../__tests__/test-utils/device-inventory-gateways.ts' ;
910import type { WebProvider } from '@agent-device/platform-web' ;
11+ import type { DaemonRequest } from '../daemon-request.ts' ;
1012import type { SessionState } from '../session-state.ts' ;
1113import { createRequestHandler } from './test-device-runtime-gateway.ts' ;
1214import { LeaseRegistry } from '../lease-registry.ts' ;
@@ -25,6 +27,19 @@ function fillWithStdinText(
2527 session : SessionState ,
2628 flags : Record < string , unknown > = { } ,
2729 fillFails ?: ( text : string ) => Error ,
30+ text = SECRET ,
31+ ) {
32+ return sendToWebSession (
33+ session ,
34+ { command : 'fill' , positionals : [ '10' , '20' , text ] , flags : { textStdin : true , ...flags } } ,
35+ fillFails ,
36+ ) ;
37+ }
38+
39+ function sendToWebSession (
40+ session : SessionState ,
41+ request : Pick < DaemonRequest , 'command' | 'positionals' | 'flags' > ,
42+ fillFails ?: ( text : string ) => Error ,
2843) {
2944 const sessionStore = makeSessionStore ( 'agent-device-router-fill-text-stdin-' ) ;
3045 sessionStore . publish ( session . name , session ) ;
@@ -65,9 +80,7 @@ function fillWithStdinText(
6580 const response = handler ( {
6681 token : 'test-token' ,
6782 session : session . name ,
68- command : 'fill' ,
69- positionals : [ '10' , '20' , SECRET ] ,
70- flags : { textStdin : true , ...flags } ,
83+ ...request ,
7184 meta : { requestId : 'req-fill-text-stdin' } ,
7285 } ) ;
7386 return { response, typed, sessionStore } ;
@@ -142,3 +155,54 @@ test('a backend error that echoes the --text-stdin value does not return it', as
142155 expect ( JSON . stringify ( result ) ) . not . toContain ( SECRET ) ;
143156 if ( ! result . ok ) expect ( result . error . message ) . toBe ( 'could not type "[REDACTED]" into the field' ) ;
144157} ) ;
158+
159+ test ( 'a backend error that echoes a --record-as stdin value shows its placeholder' , async ( ) => {
160+ const { response, typed } = fillWithStdinText (
161+ makeAuthoringSession ( 'web' , { device : WEB_DESKTOP_DEVICE } ) ,
162+ { recordAs : 'PASSWORD' } ,
163+ ( text ) => new Error ( `could not type "${ text } " into the field` ) ,
164+ ) ;
165+
166+ const result = await response ;
167+ expect ( result . ok ) . toBe ( false ) ;
168+ expect ( typed ) . toEqual ( [ SECRET ] ) ;
169+ if ( ! result . ok ) expect ( result . error . message ) . toBe ( 'could not type "${PASSWORD}" into the field' ) ;
170+ } ) ;
171+
172+ test ( 'a short --text-stdin value is redacted from the error text but not from its log path' , async ( ) => {
173+ // `stdin` also appears in this request's id, which names its diagnostics file.
174+ const { response } = fillWithStdinText (
175+ makeSession ( 'web' , { device : WEB_DESKTOP_DEVICE } ) ,
176+ { } ,
177+ ( text ) => new Error ( `could not type "${ text } " into the field` ) ,
178+ 'stdin' ,
179+ ) ;
180+
181+ const result = await response ;
182+ expect ( result . ok ) . toBe ( false ) ;
183+ if ( result . ok ) return ;
184+ expect ( result . error . message ) . toBe ( 'could not type "[REDACTED]" into the field' ) ;
185+ expect ( result . error . logPath ) . toContain ( 'req-fill-text-stdin' ) ;
186+ expect ( fs . existsSync ( result . error . logPath ! ) ) . toBe ( true ) ;
187+ expect ( result . error . diagnosticsRecord ?. requestId ) . toBe ( 'req-fill-text-stdin' ) ;
188+ } ) ;
189+
190+ test ( 'a batch step carrying --text-stdin is refused before typing and returns no part of the value' , async ( ) => {
191+ const { response, typed } = sendToWebSession ( makeSession ( 'web' , { device : WEB_DESKTOP_DEVICE } ) , {
192+ command : 'batch' ,
193+ positionals : [ ] ,
194+ flags : {
195+ batchSteps : [
196+ { command : 'fill' , positionals : [ '10' , '20' , SECRET ] , flags : { textStdin : true } } ,
197+ ] ,
198+ } ,
199+ } ) ;
200+
201+ const result = await response ;
202+ expect ( result . ok ) . toBe ( false ) ;
203+ if ( result . ok ) return ;
204+ expect ( result . error . code ) . toBe ( 'INVALID_ARGS' ) ;
205+ expect ( result . error . details ?. reason ) . toBe ( 'fill_text_stdin_in_batch' ) ;
206+ expect ( typed ) . toEqual ( [ ] ) ;
207+ expect ( JSON . stringify ( result ) ) . not . toContain ( SECRET ) ;
208+ } ) ;
0 commit comments