@@ -265,6 +265,103 @@ test('remote config defaults override generic config and env for remote workflow
265265 fs . rmSync ( root , { recursive : true , force : true } ) ;
266266} ) ;
267267
268+ test ( 'install-from-source forwards remote-config run id with explicit lease binding' , async ( ) => {
269+ const { root, home, project } = makeTempWorkspace ( ) ;
270+ const remoteConfig = path . join ( project , 'agent-device.remote.json' ) ;
271+ fs . writeFileSync (
272+ remoteConfig ,
273+ JSON . stringify ( {
274+ daemonBaseUrl : 'http://remote-mac.example.test:9124/agent-device' ,
275+ tenant : 'micha-pierzcha-a' ,
276+ sessionIsolation : 'tenant' ,
277+ runId : 'demo-run-001' ,
278+ platform : 'android' ,
279+ } ) ,
280+ 'utf8' ,
281+ ) ;
282+
283+ let stdout = '' ;
284+ let stderr = '' ;
285+ let code : number | null = null ;
286+ const calls : Array < Omit < DaemonRequest , 'token' > > = [ ] ;
287+
288+ const originalExit = process . exit ;
289+ const originalStdoutWrite = process . stdout . write . bind ( process . stdout ) ;
290+ const originalStderrWrite = process . stderr . write . bind ( process . stderr ) ;
291+ const originalCwd = process . cwd ( ) ;
292+ const previousEnv = new Map < string , string | undefined > ( ) ;
293+
294+ process . chdir ( project ) ;
295+ previousEnv . set ( 'HOME' , process . env . HOME ) ;
296+ process . env . HOME = home ;
297+
298+ ( process as any ) . exit = ( ( nextCode ?: number ) => {
299+ throw new ExitSignal ( nextCode ?? 0 ) ;
300+ } ) as typeof process . exit ;
301+ ( process . stdout as any ) . write = ( ( chunk : unknown ) => {
302+ stdout += String ( chunk ) ;
303+ return true ;
304+ } ) as typeof process . stdout . write ;
305+ ( process . stderr as any ) . write = ( ( chunk : unknown ) => {
306+ stderr += String ( chunk ) ;
307+ return true ;
308+ } ) as typeof process . stderr . write ;
309+
310+ const sendToDaemon = async ( req : Omit < DaemonRequest , 'token' > ) : Promise < DaemonResponse > => {
311+ calls . push ( req ) ;
312+ return {
313+ ok : true ,
314+ data : {
315+ launchTarget : 'com.example.demo' ,
316+ packageName : 'com.example.demo' ,
317+ } ,
318+ } ;
319+ } ;
320+
321+ try {
322+ await runCli (
323+ [
324+ 'install-from-source' ,
325+ 'https://example.com/app.apk' ,
326+ '--remote-config' ,
327+ remoteConfig ,
328+ '--lease-id' ,
329+ 'lease-demo-001' ,
330+ '--json' ,
331+ ] ,
332+ { sendToDaemon } ,
333+ ) ;
334+ } catch ( error ) {
335+ if ( error instanceof ExitSignal ) code = error . code ;
336+ else throw error ;
337+ } finally {
338+ process . exit = originalExit ;
339+ process . stdout . write = originalStdoutWrite ;
340+ process . stderr . write = originalStderrWrite ;
341+ process . chdir ( originalCwd ) ;
342+ for ( const [ key , value ] of previousEnv . entries ( ) ) {
343+ if ( value === undefined ) delete process . env [ key ] ;
344+ else process . env [ key ] = value ;
345+ }
346+ }
347+
348+ assert . equal ( code , null ) ;
349+ assert . equal ( stderr , '' ) ;
350+ const payload = JSON . parse ( stdout ) ;
351+ assert . equal ( payload . success , true ) ;
352+ assert . equal ( payload . data . launchTarget , 'com.example.demo' ) ;
353+ assert . equal ( calls . length , 1 ) ;
354+ assert . equal ( calls [ 0 ] ?. meta ?. tenantId , 'micha-pierzcha-a' ) ;
355+ assert . equal ( calls [ 0 ] ?. meta ?. runId , 'demo-run-001' ) ;
356+ assert . equal ( calls [ 0 ] ?. meta ?. leaseId , 'lease-demo-001' ) ;
357+ assert . equal ( calls [ 0 ] ?. flags ?. tenant , 'micha-pierzcha-a' ) ;
358+ assert . equal ( calls [ 0 ] ?. flags ?. runId , 'demo-run-001' ) ;
359+ assert . equal ( calls [ 0 ] ?. flags ?. leaseId , 'lease-demo-001' ) ;
360+ assert . deepEqual ( calls [ 0 ] ?. positionals , [ ] ) ;
361+
362+ fs . rmSync ( root , { recursive : true , force : true } ) ;
363+ } ) ;
364+
268365test ( 'missing explicit remote config path returns parse error before daemon dispatch' , async ( ) => {
269366 const { root, home, project } = makeTempWorkspace ( ) ;
270367
0 commit comments