@@ -35,6 +35,13 @@ const RECOVERY_DEPLOYMENT_ID = '33333333-3333-4333-8333-333333333333';
3535import { createDesktopLocalRuntimeHostRemoteAccess } from '../runtime-host-local-remote-access.js' ;
3636import type { createDesktopRuntimeHostLocalOperator } from '../runtime-host-local-operator.js' ;
3737
38+ const testOperator = ( modulePath : string ) => ( {
39+ kind : 'node' as const ,
40+ platform : 'posix' as const ,
41+ nodePath : '/usr/bin/node' ,
42+ modulePath,
43+ } ) ;
44+
3845test ( 'enabling remote access hands the same root to one managed service before Desktop resumes' , async ( t ) => {
3946 const base = await mkdtemp ( join ( tmpdir ( ) , 'maka-local-remote-access-' ) ) ;
4047 t . after ( ( ) => rm ( base , { recursive : true , force : true } ) ) ;
@@ -92,7 +99,7 @@ test('enabling remote access hands the same root to one managed service before D
9299 } ) ;
93100 return {
94101 serviceId : 'a' . repeat ( 64 ) ,
95- operatorPath : join ( base , 'operator' ) ,
102+ operator : testOperator ( join ( base , 'operator.mjs' ) ) ,
96103 rootPath,
97104 rootId : 'a' . repeat ( 64 ) ,
98105 deploymentId,
@@ -398,11 +405,11 @@ test('replaces a conflicting supervised Host with the requested active-work poli
398405 kind : 'active' ,
399406 lifecycleMode : 'supervised' ,
400407 target : {
401- schemaVersion : 1 ,
408+ schemaVersion : 2 ,
402409 serviceId : rootId ,
403410 rootPath,
404411 rootId,
405- operatorPath : join ( base , 'operator' ) ,
412+ operator : testOperator ( join ( base , 'operator.mjs' ) ) ,
406413 deploymentId : RECOVERY_DEPLOYMENT_ID ,
407414 } ,
408415 } ) ,
@@ -506,122 +513,121 @@ test('does not persist recoverable setup authority before Desktop ownership comm
506513 assert . equal ( setupCalls , 0 ) ;
507514} ) ;
508515
509- test ( 'adopts committed managed authority for every pending receipt without replaying setup' , async ( t ) => {
516+ test ( 'adopts committed managed authority from a released handoff without replaying setup' , async ( t ) => {
510517 const base = await mkdtemp ( join ( tmpdir ( ) , 'maka-local-remote-access-prestart-' ) ) ;
511518 t . after ( ( ) => rm ( base , { recursive : true , force : true } ) ) ;
512- for ( const state of [ 'handoff' , 'setupPending' ] as const ) {
513- const clientDataRoot = join ( base , state ) ;
514- const rootPath = join ( clientDataRoot , 'workspaces' , 'default' ) ;
515- const rootId = 'a' . repeat ( 64 ) ;
516- const deploymentId = '22222222-2222-4222-8222-222222222222' ;
517- const operatorPath = join ( base , 'installed' , 'operator' ) ;
518- await mkdir ( rootPath , { recursive : true } ) ;
519- await writeFile (
520- join ( clientDataRoot , 'runtime-host-local-service.json' ) ,
521- `${ JSON . stringify ( {
522- schemaVersion : 1 ,
523- state,
524- rootPath,
525- rootId,
526- coordinationRelays : [ ] ,
527- allowInterruptActiveTasks : true ,
528- } ) } \n`,
529- ) ;
530- const service = createDesktopLocalRuntimeHostRemoteAccess ( {
531- ipcMain : { handle ( ) { } , removeHandler ( ) { } } ,
532- clientDataRoot,
519+ const clientDataRoot = join ( base , 'client' ) ;
520+ const rootPath = join ( clientDataRoot , 'workspaces' , 'default' ) ;
521+ const rootId = 'a' . repeat ( 64 ) ;
522+ const deploymentId = '22222222-2222-4222-8222-222222222222' ;
523+ const installedOperator = testOperator ( join ( base , 'installed' , 'operator.mjs' ) ) ;
524+ await mkdir ( rootPath , { recursive : true } ) ;
525+ await writeFile (
526+ join ( clientDataRoot , 'runtime-host-local-service.json' ) ,
527+ `${ JSON . stringify ( {
528+ schemaVersion : 1 ,
529+ state : 'handoff' ,
533530 rootPath,
534531 rootId,
535- directPeerAvailable : false ,
536- manager : ( ) => assert . fail ( 'pre-start reconciliation must not require the Local manager' ) ,
537- resolveManagedDeploymentAuthority : async ( ) => ( {
538- kind : 'active' ,
539- lifecycleMode : 'supervised' ,
540- target : {
541- schemaVersion : 1 ,
542- serviceId : rootId ,
543- operatorPath,
544- rootPath,
545- rootId,
546- deploymentId,
547- } ,
548- } ) ,
549- resolveSetupPackage : async ( ) =>
550- assert . fail ( 'committed authority must not resolve a package' ) ,
551- operator : {
552- async runSetup ( ) {
553- assert . fail ( 'committed authority must not replay setup' ) ;
554- } ,
555- async close ( ) { } ,
556- } as unknown as ReturnType < typeof createDesktopRuntimeHostLocalOperator > ,
557- } ) ;
558- t . after ( ( ) => service . close ( ) ) ;
559-
560- assert . equal ( await service . recoverBeforeLocalHostStart ( ) , true ) ;
561- assert . deepEqual (
562- JSON . parse ( await readFile ( join ( clientDataRoot , 'runtime-host-local-service.json' ) , 'utf8' ) ) ,
563- {
564- schemaVersion : 1 ,
565- state : 'managed' ,
532+ coordinationRelays : [ ] ,
533+ allowInterruptActiveTasks : true ,
534+ } ) } \n`,
535+ ) ;
536+ const service = createDesktopLocalRuntimeHostRemoteAccess ( {
537+ ipcMain : { handle ( ) { } , removeHandler ( ) { } } ,
538+ clientDataRoot,
539+ rootPath,
540+ rootId,
541+ directPeerAvailable : false ,
542+ manager : ( ) => assert . fail ( 'pre-start reconciliation must not require the Local manager' ) ,
543+ resolveManagedDeploymentAuthority : async ( ) => ( {
544+ kind : 'active' ,
545+ lifecycleMode : 'supervised' ,
546+ target : {
547+ schemaVersion : 2 ,
566548 serviceId : rootId ,
567- operatorPath ,
549+ operator : installedOperator ,
568550 rootPath,
569551 rootId,
570552 deploymentId,
571553 } ,
572- ) ;
573- }
554+ } ) ,
555+ resolveSetupPackage : async ( ) =>
556+ assert . fail ( 'committed authority must not resolve a package' ) ,
557+ operator : {
558+ async runSetup ( ) {
559+ assert . fail ( 'committed authority must not replay setup' ) ;
560+ } ,
561+ async close ( ) { } ,
562+ } as unknown as ReturnType < typeof createDesktopRuntimeHostLocalOperator > ,
563+ } ) ;
564+ t . after ( ( ) => service . close ( ) ) ;
565+
566+ assert . equal ( await service . recoverBeforeLocalHostStart ( ) , true ) ;
567+ assert . deepEqual (
568+ JSON . parse ( await readFile ( join ( clientDataRoot , 'runtime-host-local-service.json' ) , 'utf8' ) ) ,
569+ {
570+ schemaVersion : 2 ,
571+ state : 'managed' ,
572+ serviceId : rootId ,
573+ operator : installedOperator ,
574+ rootPath,
575+ rootId,
576+ deploymentId,
577+ } ,
578+ ) ;
574579} ) ;
575580
576- test ( 'discards a legacy handoff that belongs to an externally managed Host ' , async ( t ) => {
577- const base = await mkdtemp ( join ( tmpdir ( ) , 'maka-local-remote-access-legacy-external -' ) ) ;
581+ test ( 'migrates a released managed receipt before exposing it to lifecycle operations ' , async ( t ) => {
582+ const base = await mkdtemp ( join ( tmpdir ( ) , 'maka-local-managed-migration -' ) ) ;
578583 t . after ( ( ) => rm ( base , { recursive : true , force : true } ) ) ;
579584 const clientDataRoot = join ( base , 'client' ) ;
580585 const rootPath = join ( clientDataRoot , 'workspaces' , 'default' ) ;
581586 const rootId = 'a' . repeat ( 64 ) ;
587+ const operatorPath = join ( base , 'installed' , 'operator' ) ;
582588 const lifecyclePath = join ( clientDataRoot , 'runtime-host-local-service.json' ) ;
583589 await mkdir ( rootPath , { recursive : true } ) ;
584590 await writeFile (
585591 lifecyclePath ,
586592 `${ JSON . stringify ( {
587593 schemaVersion : 1 ,
588- state : 'handoff' ,
594+ state : 'managed' ,
595+ serviceId : rootId ,
596+ operatorPath,
589597 rootPath,
590598 rootId,
591- coordinationRelays : [ ] ,
592- allowInterruptActiveTasks : false ,
599+ deploymentId : RECOVERY_DEPLOYMENT_ID ,
593600 } ) } \n`,
594601 ) ;
595- let setupCalls = 0 ;
596602 const service = createDesktopLocalRuntimeHostRemoteAccess ( {
597603 ipcMain : { handle ( ) { } , removeHandler ( ) { } } ,
598604 clientDataRoot,
599605 rootPath,
600606 rootId,
601607 directPeerAvailable : true ,
602- manager : ( ) =>
603- ( {
604- async retireOwnedLocalHost ( ) {
605- return { kind : 'not_owned' as const } ;
606- } ,
607- } ) as unknown as RuntimeHostDesktopManager ,
608- resolveManagedDeploymentAuthority : async ( ) => undefined ,
608+ manager : ( ) => undefined ,
609609 resolveSetupPackage : async ( ) => ( { kind : 'npm' , specifier : 'maka-agent@0.2.0' } ) ,
610610 operator : {
611- async runSetup ( ) {
612- setupCalls += 1 ;
613- throw new Error ( 'setup must not replace an externally managed Host' ) ;
614- } ,
615611 async close ( ) { } ,
616612 } as unknown as ReturnType < typeof createDesktopRuntimeHostLocalOperator > ,
617613 } ) ;
618614 t . after ( ( ) => service . close ( ) ) ;
619615
620- assert . equal ( await service . recoverBeforeLocalHostStart ( ) , false ) ;
621- await service . recover ( ) ;
622-
623- assert . equal ( setupCalls , 0 ) ;
624- await assert . rejects ( readFile ( lifecyclePath , 'utf8' ) , { code : 'ENOENT' } ) ;
616+ const target = await service . inspectManaged ( async ( managed ) => managed ) ;
617+ const expected = {
618+ schemaVersion : 2 ,
619+ state : 'managed' ,
620+ serviceId : rootId ,
621+ operator : {
622+ kind : 'legacy_posix_executable' ,
623+ executablePath : operatorPath ,
624+ } ,
625+ rootPath,
626+ rootId,
627+ deploymentId : RECOVERY_DEPLOYMENT_ID ,
628+ } ;
629+ assert . deepEqual ( target , expected ) ;
630+ assert . deepEqual ( JSON . parse ( await readFile ( lifecyclePath , 'utf8' ) ) , expected ) ;
625631} ) ;
626632
627633test ( 'interrupted Local Host setup converges to its exact managed service' , async ( t ) => {
@@ -634,7 +640,7 @@ test('interrupted Local Host setup converges to its exact managed service', asyn
634640 await writeFile (
635641 join ( clientDataRoot , 'runtime-host-local-service.json' ) ,
636642 `${ JSON . stringify ( {
637- schemaVersion : 1 ,
643+ schemaVersion : 2 ,
638644 state : 'setupPending' ,
639645 rootPath,
640646 rootId,
@@ -668,7 +674,7 @@ test('interrupted Local Host setup converges to its exact managed service', asyn
668674 setupCalls += 1 ;
669675 return {
670676 serviceId : rootId ,
671- operatorPath : join ( base , 'operator' ) ,
677+ operator : testOperator ( join ( base , 'operator.mjs' ) ) ,
672678 rootPath,
673679 rootId,
674680 deploymentId : '22222222-2222-4222-8222-222222222222' ,
@@ -708,10 +714,10 @@ test('startup replays the persisted peer intent instead of gating recovery on st
708714 await writeFile (
709715 join ( clientDataRoot , 'runtime-host-local-service.json' ) ,
710716 `${ JSON . stringify ( {
711- schemaVersion : 1 ,
717+ schemaVersion : 2 ,
712718 state : 'peerChanging' ,
713719 serviceId : 'b' . repeat ( 64 ) ,
714- operatorPath : join ( clientDataRoot , 'operator' ) ,
720+ operator : testOperator ( join ( clientDataRoot , 'operator.mjs' ) ) ,
715721 rootPath,
716722 rootId,
717723 deploymentId : RECOVERY_DEPLOYMENT_ID ,
@@ -835,10 +841,10 @@ test('pre-start recovery cleans a committed uninstall before an ephemeral Host c
835841 await writeFile (
836842 join ( clientDataRoot , 'runtime-host-local-service.json' ) ,
837843 `${ JSON . stringify ( {
838- schemaVersion : 1 ,
844+ schemaVersion : 2 ,
839845 state : 'uninstalling' ,
840846 serviceId : 'b' . repeat ( 64 ) ,
841- operatorPath : join ( base , 'operator' ) ,
847+ operator : testOperator ( join ( base , 'operator.mjs' ) ) ,
842848 rootPath,
843849 rootId,
844850 deploymentId : RECOVERY_DEPLOYMENT_ID ,
@@ -897,10 +903,10 @@ test('pre-start recovery settles a canonical uninstall transition through its ex
897903 await writeFile (
898904 join ( clientDataRoot , 'runtime-host-local-service.json' ) ,
899905 `${ JSON . stringify ( {
900- schemaVersion : 1 ,
906+ schemaVersion : 2 ,
901907 state : 'uninstalling' ,
902908 serviceId : 'b' . repeat ( 64 ) ,
903- operatorPath : join ( base , 'operator' ) ,
909+ operator : testOperator ( join ( base , 'operator.mjs' ) ) ,
904910 rootPath,
905911 rootId,
906912 deploymentId : RECOVERY_DEPLOYMENT_ID ,
@@ -955,10 +961,10 @@ async function writeManagedLifecycle(
955961 await writeFile (
956962 join ( clientDataRoot , 'runtime-host-local-service.json' ) ,
957963 `${ JSON . stringify ( {
958- schemaVersion : 1 ,
964+ schemaVersion : 2 ,
959965 state : 'managed' ,
960966 serviceId : 'b' . repeat ( 64 ) ,
961- operatorPath : join ( clientDataRoot , 'operator' ) ,
967+ operator : testOperator ( join ( clientDataRoot , 'operator.mjs' ) ) ,
962968 rootPath,
963969 rootId,
964970 deploymentId : RECOVERY_DEPLOYMENT_ID ,
0 commit comments