Skip to content
Source
Sign inSign up

device.ts

120 lines4.4 KB

1import { type DeviceToolVersions, WS_METHODS } from "@t3tools/contracts";
2import { Atom } from "effect/reactivity";
3​
4import type { EnvironmentRegistry } from "../connection/registry.ts";
5import {
6 createAtomCommandScheduler,
7 createEnvironmentRpcCommand,
8 createEnvironmentRpcSubscriptionAtomFamily,
9} from "./runtime.ts";
10​
11export function createDeviceEnvironmentAtoms<R, E>(
12 runtime: Atom.AtomRuntime<EnvironmentRegistry | R, E>,
13) {
14 const scheduler = createAtomCommandScheduler();
15 const concurrency = {
16 mode: "serial" as const,
17 key: ({ environmentId }: { environmentId: string }) => environmentId,
18 };
19 // An EAS Simulator start can take minutes; it must not hold up powering off
20 // another device or turning device support off, so each device opens in its
21 // own lane. Every EAS start opens the same catalog device, so the thread is
22 // part of the lane too.
23 const openConcurrency = {
24 mode: "serial" as const,
25 key: ({
26 environmentId,
27 input,
28 }: {
29 environmentId: string;
30 input: {
31 readonly threadId: string;
32 readonly hostId?: string | undefined;
33 readonly deviceId: string;
34 };
35 }) =>
36 JSON.stringify([environmentId, "open", input.threadId, input.hostId ?? null, input.deviceId]),
37 };
38 return {
39 state: createEnvironmentRpcSubscriptionAtomFamily(runtime, {
40 label: "environment-data:device:state",
41 tag: WS_METHODS.subscribeDeviceState,
42 }),
43 configure: createEnvironmentRpcCommand(runtime, {
44 label: "environment-data:device:configure",
45 tag: WS_METHODS.deviceConfigure,
46 scheduler,
47 concurrency,
48 }),
49 testHost: createEnvironmentRpcCommand(runtime, {
50 label: "environment-data:device:test-host",
51 tag: WS_METHODS.deviceTestHost,
52 }),
53 easAccount: createEnvironmentRpcCommand(runtime, {
54 label: "environment-data:device:eas-account",
55 tag: WS_METHODS.deviceEasAccount,
56 }),
57 list: createEnvironmentRpcCommand(runtime, {
58 label: "environment-data:device:list",
59 tag: WS_METHODS.deviceList,
60 scheduler,
61 concurrency,
62 }),
63 open: createEnvironmentRpcCommand(runtime, {
64 label: "environment-data:device:open",
65 tag: WS_METHODS.deviceOpen,
66 scheduler,
67 concurrency: openConcurrency,
68 }),
69 close: createEnvironmentRpcCommand(runtime, {
70 label: "environment-data:device:close",
71 tag: WS_METHODS.deviceClose,
72 scheduler,
73 concurrency,
74 }),
75 shutdown: createEnvironmentRpcCommand(runtime, {
76 label: "environment-data:device:shutdown",
77 tag: WS_METHODS.deviceShutdown,
78 scheduler,
79 concurrency,
80 }),
81 detail: createEnvironmentRpcCommand(runtime, {
82 label: "environment-data:device:detail",
83 tag: WS_METHODS.deviceDetail,
84 scheduler,
85 concurrency,
86 }),
87 action: createEnvironmentRpcCommand(runtime, {
88 label: "environment-data:device:action",
89 tag: WS_METHODS.deviceAction,
90 scheduler,
91 concurrency,
92 }),
93 };
95​
96export function deviceToolVersionLabels(tools: DeviceToolVersions | undefined) {
97 if (!tools) return ["Device tool versions have not been checked."];
98 return (
99 [
100 ["Device hub", tools.hub],
101 ["Agent tools", tools.agent],
102 ] as const
103 ).map(([name, tool]) => {
104 const installed = tool.installedVersions.length ? tool.installedVersions.join(", ") : "none";
105 return `${name}: installed ${installed}; required ${tool.requiredVersion}${tool.runningVersion ? `; running ${tool.runningVersion}` : ""}.`;
106 });
108​
109export function deviceToolUpdatePolicy(tools: DeviceToolVersions | undefined) {
110 if (!tools) return "Versions have not been checked. Reconnect the host and check versions.";
111 const outdated = [tools.hub, tools.agent].filter(
112 (tool) =>
113 tool.installedVersions.length > 0 && !tool.installedVersions.includes(tool.requiredVersion),
114 );
115 return outdated.length > 0
116 ? "Update pending. Required tools will install automatically when next used. The host needs network access; an older install is not used as a fallback."
117 : "Required tools are installed automatically when needed. Checking versions does not install or start anything.";
119export const deviceToolUpdateOwnership =
120 "This environment's T3 server chooses device tool versions for itself and its SSH hosts. Update that server to receive newer tool versions; updating only your browser or mobile app does not update a remote server.";