Skip to content

Commit ec6a4e7

Browse files
committed
fix(server): keep disabled device status during late startup
1 parent 5a1a98f commit ec6a4e7

2 files changed

Lines changed: 47 additions & 25 deletions

File tree

‎apps/server/src/device/DeviceMultiHost.test.ts‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,19 @@ it.effect("stops a start that fails after device support was turned off before i
240240
platformAvailability: (platform) => Effect.succeed({ platform, available: true }),
241241
ensureReady: () => Effect.succeed(ready),
242242
// Like LocalDeviceHost: the hub is up before the agent tools install fails.
243-
ensureAgentReady: () =>
243+
ensureAgentReady: (onPhase) =>
244244
Effect.sync(() => {
245245
running = true;
246246
}).pipe(
247+
Effect.andThen(onPhase("installing")),
248+
Effect.tap(() =>
249+
service.state.pipe(
250+
Effect.map((state) => {
251+
expect(state.hostStatus).toBe("disabled");
252+
expect(state.hostStatuses).toEqual({});
253+
}),
254+
),
255+
),
247256
Effect.andThen(
248257
Effect.fail(
249258
new DeviceHost.DeviceHostError({

‎apps/server/src/device/DeviceService.ts‎

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -359,14 +359,25 @@ export const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function*
359359
);
360360

361361
// Uninterruptible so a committed retirement always prunes, even if its caller is cancelled.
362-
const publish = (update: (state: DeviceServiceState) => DeviceServiceState) =>
363-
SynchronizedRef.modifyEffect(stateRef, ({ state }) => {
364-
const next = { ...update(state), revision: state.revision + 1 };
365-
const retired = state.sessions.some((session) => !next.sessions.includes(session));
366-
return PubSub.publish(statePubSub, next).pipe(
367-
Effect.as([{ next, retired }, { state: next }] as const),
368-
);
369-
}).pipe(
362+
const publish = (
363+
update: (state: DeviceServiceState) => DeviceServiceState,
364+
onlyWhileEnabled = false,
365+
) =>
366+
SynchronizedRef.modifyEffect(stateRef, ({ state }) =>
367+
Effect.gen(function* () {
368+
const enabled =
369+
!onlyWhileEnabled ||
370+
(yield* readDeviceSettings.pipe(
371+
Effect.map((current) => current.enabled),
372+
Effect.orElseSucceed(() => false),
373+
));
374+
const next = { ...(enabled ? update(state) : state), revision: state.revision + 1 };
375+
const retired = state.sessions.some((session) => !next.sessions.includes(session));
376+
return yield* PubSub.publish(statePubSub, next).pipe(
377+
Effect.as([{ next, retired }, { state: next }] as const),
378+
);
379+
}),
380+
).pipe(
370381
Effect.tap(({ retired }) => (retired ? pruneLaunchers : Effect.void)),
371382
Effect.map(({ next }) => next),
372383
Effect.uninterruptible,
@@ -391,24 +402,26 @@ export const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function*
391402
Effect.suspend(() =>
392403
!hosts.has(hostId)
393404
? SynchronizedRef.get(stateRef).pipe(Effect.map(({ state }) => state))
394-
: publish((state) =>
395-
expectedHost && hosts.get(hostId) !== expectedHost
396-
? state
397-
: {
398-
...state,
399-
...(hostId === LOCAL_DEVICE_HOST_ID
400-
? { hostStatus: status.status, hostStatusDetail: status.detail }
401-
: {}),
402-
hostStatuses: {
403-
...state.hostStatuses,
404-
[hostId]: {
405-
...(state.hostStatuses[hostId]?.androidDiscoveryIncomplete
406-
? { androidDiscoveryIncomplete: true }
407-
: {}),
408-
...status,
405+
: publish(
406+
(state) =>
407+
expectedHost && hosts.get(hostId) !== expectedHost
408+
? state
409+
: {
410+
...state,
411+
...(hostId === LOCAL_DEVICE_HOST_ID
412+
? { hostStatus: status.status, hostStatusDetail: status.detail }
413+
: {}),
414+
hostStatuses: {
415+
...state.hostStatuses,
416+
[hostId]: {
417+
...(state.hostStatuses[hostId]?.androidDiscoveryIncomplete
418+
? { androidDiscoveryIncomplete: true }
419+
: {}),
420+
...status,
421+
},
409422
},
410423
},
411-
},
424+
true,
412425
),
413426
);
414427

0 commit comments

Comments
 (0)