@dxos/plugin-client 0.9.0 → 0.9.1-staging.ee54ba693a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/dist/lib/neutral/{AccountContainer-FZ3IC6H4.mjs → AccountContainer-7SF53UON.mjs} +17 -13
  2. package/dist/lib/neutral/AccountContainer-7SF53UON.mjs.map +7 -0
  3. package/dist/lib/neutral/ClientPlugin.node.mjs +477 -370
  4. package/dist/lib/neutral/ClientPlugin.node.mjs.map +4 -4
  5. package/dist/lib/neutral/{DevicesContainer-6MWNUC3I.mjs → DevicesContainer-PRWIN36N.mjs} +36 -27
  6. package/dist/lib/neutral/DevicesContainer-PRWIN36N.mjs.map +7 -0
  7. package/dist/lib/neutral/{InvitationsContainer-ME722VKS.mjs → InvitationsContainer-BI5RAWET.mjs} +43 -32
  8. package/dist/lib/neutral/InvitationsContainer-BI5RAWET.mjs.map +7 -0
  9. package/dist/lib/neutral/{JoinDialog-2NXAKPLP.mjs → JoinDialog-4BWV4CYB.mjs} +2 -2
  10. package/dist/lib/neutral/JoinDialog-4BWV4CYB.mjs.map +7 -0
  11. package/dist/lib/neutral/{ProfileContainer-VPX3FPWG.mjs → ProfileContainer-YEAYOTX5.mjs} +22 -21
  12. package/dist/lib/neutral/ProfileContainer-YEAYOTX5.mjs.map +7 -0
  13. package/dist/lib/neutral/{RecoveryCodeDialog-EWHB6U5T.mjs → RecoveryCodeDialog-7EPNKCLB.mjs} +2 -2
  14. package/dist/lib/neutral/RecoveryCodeDialog-7EPNKCLB.mjs.map +7 -0
  15. package/dist/lib/neutral/{RecoveryCredentialsContainer-IPBGDMIA.mjs → RecoveryCredentialsContainer-6BZ2IWX7.mjs} +24 -15
  16. package/dist/lib/neutral/RecoveryCredentialsContainer-6BZ2IWX7.mjs.map +7 -0
  17. package/dist/lib/neutral/UsageContainer-2DWCVVE4.mjs +241 -0
  18. package/dist/lib/neutral/UsageContainer-2DWCVVE4.mjs.map +7 -0
  19. package/dist/lib/neutral/account-cache-YVQKLNV2.mjs +16 -0
  20. package/dist/lib/neutral/account-cache-YVQKLNV2.mjs.map +7 -0
  21. package/dist/lib/neutral/{app-graph-builder-A74W7C36.mjs → app-graph-builder-RVXTD6OE.mjs} +30 -16
  22. package/dist/lib/neutral/app-graph-builder-RVXTD6OE.mjs.map +7 -0
  23. package/dist/lib/neutral/capabilities/index.mjs +6 -6
  24. package/dist/lib/neutral/capabilities/node.mjs +3 -3
  25. package/dist/lib/neutral/chunk-6G4KZLBG.mjs +28 -0
  26. package/dist/lib/neutral/chunk-6G4KZLBG.mjs.map +7 -0
  27. package/dist/lib/neutral/{chunk-D6HPCLTS.mjs → chunk-7DEIKHB7.mjs} +16 -2
  28. package/dist/lib/neutral/chunk-7DEIKHB7.mjs.map +7 -0
  29. package/dist/lib/neutral/chunk-A7WDDIWY.mjs +162 -0
  30. package/dist/lib/neutral/chunk-A7WDDIWY.mjs.map +7 -0
  31. package/dist/lib/neutral/chunk-GSKMAS3S.mjs +13 -0
  32. package/dist/lib/neutral/chunk-GSKMAS3S.mjs.map +7 -0
  33. package/dist/lib/neutral/{chunk-JEF2FQGB.mjs → chunk-JY35S2CN.mjs} +2 -2
  34. package/dist/lib/neutral/{chunk-JEF2FQGB.mjs.map → chunk-JY35S2CN.mjs.map} +1 -1
  35. package/dist/lib/neutral/chunk-ZFABGG2E.mjs +8 -0
  36. package/dist/lib/neutral/{chunk-NTVDGDQ3.mjs.map → chunk-ZFABGG2E.mjs.map} +2 -2
  37. package/dist/lib/neutral/containers/index.mjs +10 -8
  38. package/dist/lib/neutral/containers/index.mjs.map +3 -3
  39. package/dist/lib/neutral/{create-agent-6HLW4D3A.mjs → create-agent-6UKJMZS5.mjs} +3 -3
  40. package/dist/lib/neutral/{create-identity-JIWAR5GW.mjs → create-identity-ULCRB3YH.mjs} +3 -3
  41. package/dist/lib/neutral/{create-passkey-FOXEMXGF.mjs → create-passkey-I7VDS3AC.mjs} +7 -7
  42. package/dist/lib/neutral/create-passkey-I7VDS3AC.mjs.map +7 -0
  43. package/dist/lib/neutral/{create-recovery-code-ZWDKLPH4.mjs → create-recovery-code-DH2B6EGP.mjs} +4 -4
  44. package/dist/lib/neutral/index.mjs +6 -4
  45. package/dist/lib/neutral/{join-identity-QEQFAE2S.mjs → join-identity-PJCHDNLH.mjs} +3 -3
  46. package/dist/lib/neutral/{layer-specs-CQLXQBQJ.mjs → layer-specs-YI7BVWKZ.mjs} +6 -8
  47. package/dist/lib/neutral/layer-specs-YI7BVWKZ.mjs.map +7 -0
  48. package/dist/lib/neutral/meta.json +1 -1
  49. package/dist/lib/neutral/meta.mjs +1 -1
  50. package/dist/lib/neutral/{navigation-handler-LU26CDFR.mjs → navigation-handler-MQ6MZXLX.mjs} +3 -3
  51. package/dist/lib/neutral/open-usage-OJVTPGOY.mjs +27 -0
  52. package/dist/lib/neutral/open-usage-OJVTPGOY.mjs.map +7 -0
  53. package/dist/lib/neutral/operations/index.mjs +2 -2
  54. package/dist/lib/neutral/plugin.mjs +3 -3
  55. package/dist/lib/neutral/{react-context-B5LXMHVM.mjs → react-context-V74T6JOJ.mjs} +2 -2
  56. package/dist/lib/neutral/{react-context-B5LXMHVM.mjs.map → react-context-V74T6JOJ.mjs.map} +3 -3
  57. package/dist/lib/neutral/{react-surface-DEKGWTCD.mjs → react-surface-HPXRA65U.mjs} +8 -3
  58. package/dist/lib/neutral/react-surface-HPXRA65U.mjs.map +7 -0
  59. package/dist/lib/neutral/{recover-identity-JNFKLCUD.mjs → recover-identity-B7YM2BR4.mjs} +3 -3
  60. package/dist/lib/neutral/{redeem-passkey-4SBAI4AG.mjs → redeem-passkey-TYP4XLHO.mjs} +10 -10
  61. package/dist/lib/neutral/redeem-passkey-TYP4XLHO.mjs.map +7 -0
  62. package/dist/lib/neutral/{redeem-token-POBD6TMN.mjs → redeem-token-2J33R3PV.mjs} +3 -3
  63. package/dist/lib/neutral/{reset-storage-LQKUV5RA.mjs → reset-storage-K47HZTGP.mjs} +3 -3
  64. package/dist/lib/neutral/{share-identity-KNQ2XNXV.mjs → share-identity-TOROBLWL.mjs} +6 -6
  65. package/dist/lib/neutral/share-identity-TOROBLWL.mjs.map +7 -0
  66. package/dist/lib/neutral/testing/index.mjs +2 -2
  67. package/dist/lib/neutral/testing/index.mjs.map +3 -3
  68. package/dist/lib/neutral/translations.mjs +24 -1
  69. package/dist/lib/neutral/translations.mjs.map +3 -3
  70. package/dist/lib/neutral/types/index.mjs +3 -1
  71. package/dist/types/dx.config.d.ts +28 -0
  72. package/dist/types/dx.config.d.ts.map +1 -0
  73. package/dist/types/src/capabilities/account-cache.d.ts +1 -31
  74. package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -2
  75. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  76. package/dist/types/src/capabilities/client.d.ts +1 -2
  77. package/dist/types/src/capabilities/client.d.ts.map +1 -1
  78. package/dist/types/src/capabilities/hub-http-client.d.ts +1 -2
  79. package/dist/types/src/capabilities/hub-http-client.d.ts.map +1 -1
  80. package/dist/types/src/capabilities/index.d.ts +9 -43
  81. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  82. package/dist/types/src/capabilities/layer-specs.d.ts +1 -2
  83. package/dist/types/src/capabilities/layer-specs.d.ts.map +1 -1
  84. package/dist/types/src/capabilities/migrations.d.ts +1 -1
  85. package/dist/types/src/capabilities/navigation-handler/index.d.ts +1 -1
  86. package/dist/types/src/capabilities/navigation-handler/index.d.ts.map +1 -1
  87. package/dist/types/src/capabilities/navigation-handler/navigation-handler.d.ts +1 -2
  88. package/dist/types/src/capabilities/navigation-handler/navigation-handler.d.ts.map +1 -1
  89. package/dist/types/src/capabilities/node.d.ts +6 -6
  90. package/dist/types/src/capabilities/node.d.ts.map +1 -1
  91. package/dist/types/src/capabilities/react-context.d.ts +1 -6
  92. package/dist/types/src/capabilities/react-context.d.ts.map +1 -1
  93. package/dist/types/src/capabilities/react-surface.d.ts +2 -2
  94. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  95. package/dist/types/src/capabilities/schema-defs.d.ts +1 -1
  96. package/dist/types/src/commands/account/index.d.ts +3 -0
  97. package/dist/types/src/commands/account/index.d.ts.map +1 -0
  98. package/dist/types/src/commands/account/login/index.d.ts +2 -0
  99. package/dist/types/src/commands/account/login/index.d.ts.map +1 -0
  100. package/dist/types/src/commands/account/login/login.d.ts +12 -0
  101. package/dist/types/src/commands/account/login/login.d.ts.map +1 -0
  102. package/dist/types/src/commands/account/logout/index.d.ts +2 -0
  103. package/dist/types/src/commands/account/logout/index.d.ts.map +1 -0
  104. package/dist/types/src/commands/account/logout/logout.d.ts +9 -0
  105. package/dist/types/src/commands/account/logout/logout.d.ts.map +1 -0
  106. package/dist/types/src/commands/halo/join/join.d.ts +1 -1
  107. package/dist/types/src/commands/halo/join/join.d.ts.map +1 -1
  108. package/dist/types/src/commands/halo/util.d.ts +4 -0
  109. package/dist/types/src/commands/halo/util.d.ts.map +1 -1
  110. package/dist/types/src/commands/index.d.ts +1 -0
  111. package/dist/types/src/commands/index.d.ts.map +1 -1
  112. package/dist/types/src/constants.d.ts.map +1 -1
  113. package/dist/types/src/containers/AccountContainer/AccountContainer.d.ts.map +1 -1
  114. package/dist/types/src/containers/AccountContainer/AccountContainer.stories.d.ts +14 -0
  115. package/dist/types/src/containers/AccountContainer/AccountContainer.stories.d.ts.map +1 -0
  116. package/dist/types/src/containers/DevicesContainer/DevicesContainer.d.ts.map +1 -1
  117. package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.d.ts.map +1 -1
  118. package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.stories.d.ts +14 -0
  119. package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.stories.d.ts.map +1 -0
  120. package/dist/types/src/containers/ProfileContainer/ProfileContainer.d.ts.map +1 -1
  121. package/dist/types/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.d.ts.map +1 -1
  122. package/dist/types/src/containers/UsageContainer/UsageContainer.d.ts +7 -0
  123. package/dist/types/src/containers/UsageContainer/UsageContainer.d.ts.map +1 -0
  124. package/dist/types/src/containers/UsageContainer/UsageView.d.ts +28 -0
  125. package/dist/types/src/containers/UsageContainer/UsageView.d.ts.map +1 -0
  126. package/dist/types/src/containers/UsageContainer/UsageView.stories.d.ts +21 -0
  127. package/dist/types/src/containers/UsageContainer/UsageView.stories.d.ts.map +1 -0
  128. package/dist/types/src/containers/UsageContainer/index.d.ts +2 -0
  129. package/dist/types/src/containers/UsageContainer/index.d.ts.map +1 -0
  130. package/dist/types/src/containers/index.d.ts +1 -0
  131. package/dist/types/src/containers/index.d.ts.map +1 -1
  132. package/dist/types/src/hooks/index.d.ts +2 -0
  133. package/dist/types/src/hooks/index.d.ts.map +1 -0
  134. package/dist/types/src/{state/use-hub-http.d.ts → hooks/useHubClient.d.ts} +1 -1
  135. package/dist/types/src/hooks/useHubClient.d.ts.map +1 -0
  136. package/dist/types/src/meta.d.ts +32 -2
  137. package/dist/types/src/meta.d.ts.map +1 -1
  138. package/dist/types/src/operations/create-passkey.d.ts.map +1 -1
  139. package/dist/types/src/operations/definitions.d.ts +1 -0
  140. package/dist/types/src/operations/definitions.d.ts.map +1 -1
  141. package/dist/types/src/operations/index.d.ts.map +1 -1
  142. package/dist/types/src/operations/open-usage.d.ts +5 -0
  143. package/dist/types/src/operations/open-usage.d.ts.map +1 -0
  144. package/dist/types/src/testing/initializeIdentity.d.ts.map +1 -1
  145. package/dist/types/src/translations.d.ts.map +1 -1
  146. package/dist/types/src/{state → types}/account-cache.d.ts.map +1 -1
  147. package/dist/types/src/types/capabilities.d.ts.map +1 -1
  148. package/dist/types/src/types/events.d.ts.map +1 -1
  149. package/dist/types/src/types/index.d.ts +1 -0
  150. package/dist/types/src/types/index.d.ts.map +1 -1
  151. package/dist/types/src/types/schema.d.ts +1 -0
  152. package/dist/types/src/types/schema.d.ts.map +1 -1
  153. package/dist/types/tsconfig.tsbuildinfo +1 -1
  154. package/dx.config.ts +19 -0
  155. package/package.json +47 -44
  156. package/src/ClientPlugin.node.ts +2 -2
  157. package/src/ClientPlugin.test.ts +1 -1
  158. package/src/capabilities/account-cache.ts +1 -3
  159. package/src/capabilities/app-graph-builder.ts +81 -71
  160. package/src/capabilities/layer-specs.ts +4 -6
  161. package/src/capabilities/react-context.tsx +1 -1
  162. package/src/capabilities/react-surface.tsx +6 -0
  163. package/src/commands/account/index.ts +13 -0
  164. package/src/commands/account/login/index.ts +5 -0
  165. package/src/commands/account/login/login.ts +147 -0
  166. package/src/commands/account/logout/index.ts +5 -0
  167. package/src/commands/account/logout/logout.ts +49 -0
  168. package/src/commands/halo/create/create.ts +2 -2
  169. package/src/commands/halo/index.ts +6 -6
  170. package/src/commands/halo/join/join.ts +2 -13
  171. package/src/commands/halo/util.ts +24 -0
  172. package/src/commands/index.ts +1 -0
  173. package/src/constants.ts +3 -3
  174. package/src/containers/AccountContainer/AccountContainer.stories.tsx +46 -0
  175. package/src/containers/AccountContainer/AccountContainer.tsx +67 -63
  176. package/src/containers/DevicesContainer/DevicesContainer.tsx +72 -54
  177. package/src/containers/InvitationsContainer/InvitationsContainer.stories.tsx +46 -0
  178. package/src/containers/InvitationsContainer/InvitationsContainer.tsx +65 -62
  179. package/src/containers/JoinDialog/JoinDialog.tsx +1 -1
  180. package/src/containers/ProfileContainer/ProfileContainer.tsx +38 -28
  181. package/src/containers/RecoveryCodeDialog/RecoveryCodeDialog.tsx +1 -1
  182. package/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.tsx +52 -43
  183. package/src/containers/UsageContainer/UsageContainer.tsx +48 -0
  184. package/src/containers/UsageContainer/UsageView.stories.tsx +127 -0
  185. package/src/containers/UsageContainer/UsageView.tsx +270 -0
  186. package/src/containers/UsageContainer/index.ts +5 -0
  187. package/src/containers/index.ts +1 -0
  188. package/src/hooks/index.ts +5 -0
  189. package/src/meta.ts +3 -12
  190. package/src/operations/create-passkey.ts +6 -4
  191. package/src/operations/definitions.ts +12 -1
  192. package/src/operations/index.ts +1 -0
  193. package/src/operations/open-usage.ts +24 -0
  194. package/src/operations/redeem-passkey.ts +7 -7
  195. package/src/operations/share-identity.ts +3 -3
  196. package/src/testing/initializeIdentity.ts +5 -2
  197. package/src/translations.ts +25 -1
  198. package/src/types/capabilities.ts +8 -6
  199. package/src/types/events.ts +5 -5
  200. package/src/types/index.ts +1 -0
  201. package/src/types/schema.ts +23 -16
  202. package/dist/lib/neutral/AccountContainer-FZ3IC6H4.mjs.map +0 -7
  203. package/dist/lib/neutral/DevicesContainer-6MWNUC3I.mjs.map +0 -7
  204. package/dist/lib/neutral/InvitationsContainer-ME722VKS.mjs.map +0 -7
  205. package/dist/lib/neutral/JoinDialog-2NXAKPLP.mjs.map +0 -7
  206. package/dist/lib/neutral/ProfileContainer-VPX3FPWG.mjs.map +0 -7
  207. package/dist/lib/neutral/RecoveryCodeDialog-EWHB6U5T.mjs.map +0 -7
  208. package/dist/lib/neutral/RecoveryCredentialsContainer-IPBGDMIA.mjs.map +0 -7
  209. package/dist/lib/neutral/account-cache-4EKGEUJI.mjs +0 -43
  210. package/dist/lib/neutral/account-cache-4EKGEUJI.mjs.map +0 -7
  211. package/dist/lib/neutral/app-graph-builder-A74W7C36.mjs.map +0 -7
  212. package/dist/lib/neutral/chunk-D6HPCLTS.mjs.map +0 -7
  213. package/dist/lib/neutral/chunk-JOCCVODY.mjs +0 -21
  214. package/dist/lib/neutral/chunk-JOCCVODY.mjs.map +0 -7
  215. package/dist/lib/neutral/chunk-NTVDGDQ3.mjs +0 -8
  216. package/dist/lib/neutral/chunk-TUK3ELUU.mjs +0 -135
  217. package/dist/lib/neutral/chunk-TUK3ELUU.mjs.map +0 -7
  218. package/dist/lib/neutral/chunk-WWCVZG3K.mjs +0 -13
  219. package/dist/lib/neutral/chunk-WWCVZG3K.mjs.map +0 -7
  220. package/dist/lib/neutral/create-passkey-FOXEMXGF.mjs.map +0 -7
  221. package/dist/lib/neutral/layer-specs-CQLXQBQJ.mjs.map +0 -7
  222. package/dist/lib/neutral/react-surface-DEKGWTCD.mjs.map +0 -7
  223. package/dist/lib/neutral/redeem-passkey-4SBAI4AG.mjs.map +0 -7
  224. package/dist/lib/neutral/share-identity-KNQ2XNXV.mjs.map +0 -7
  225. package/dist/types/src/state/use-hub-http.d.ts.map +0 -1
  226. /package/dist/lib/neutral/{create-agent-6HLW4D3A.mjs.map → create-agent-6UKJMZS5.mjs.map} +0 -0
  227. /package/dist/lib/neutral/{create-identity-JIWAR5GW.mjs.map → create-identity-ULCRB3YH.mjs.map} +0 -0
  228. /package/dist/lib/neutral/{create-recovery-code-ZWDKLPH4.mjs.map → create-recovery-code-DH2B6EGP.mjs.map} +0 -0
  229. /package/dist/lib/neutral/{join-identity-QEQFAE2S.mjs.map → join-identity-PJCHDNLH.mjs.map} +0 -0
  230. /package/dist/lib/neutral/{navigation-handler-LU26CDFR.mjs.map → navigation-handler-MQ6MZXLX.mjs.map} +0 -0
  231. /package/dist/lib/neutral/{recover-identity-JNFKLCUD.mjs.map → recover-identity-B7YM2BR4.mjs.map} +0 -0
  232. /package/dist/lib/neutral/{redeem-token-POBD6TMN.mjs.map → redeem-token-2J33R3PV.mjs.map} +0 -0
  233. /package/dist/lib/neutral/{reset-storage-LQKUV5RA.mjs.map → reset-storage-K47HZTGP.mjs.map} +0 -0
  234. /package/dist/types/src/{state → types}/account-cache.d.ts +0 -0
  235. /package/src/{state/use-hub-http.ts → hooks/useHubClient.ts} +0 -0
  236. /package/src/{state → types}/account-cache.ts +0 -0
@@ -9,7 +9,7 @@ import * as Effect from 'effect/Effect';
9
9
  import * as Option from 'effect/Option';
10
10
 
11
11
  import { Capabilities, Plugin } from '@dxos/app-framework';
12
- import { PERSONAL_SPACE_TAG } from '@dxos/app-toolkit';
12
+ import { AppSpace } from '@dxos/app-toolkit';
13
13
  import { CommandConfig, flushAndSync, spaceLayer } from '@dxos/cli-util';
14
14
  import { print } from '@dxos/cli-util';
15
15
  import { ClientService } from '@dxos/client';
@@ -45,7 +45,7 @@ export const handler = Effect.fn(function* ({
45
45
 
46
46
  // Create personal space for the CLI identity.
47
47
  const space = yield* Effect.promise(() =>
48
- client.spaces.create({}, { tags: [PERSONAL_SPACE_TAG], membershipPolicy: MembershipPolicy.LOCKED }),
48
+ client.spaces.create({}, { tags: [AppSpace.PERSONAL_SPACE_TAG], membershipPolicy: MembershipPolicy.LOCKED }),
49
49
  );
50
50
  yield* Effect.promise(() => space.waitUntilReady());
51
51
  yield* Effect.promise(() => space.internal.setEdgeReplicationPreference(EdgeReplicationSetting.ENABLED));
@@ -4,18 +4,18 @@
4
4
 
5
5
  import * as Command from '@effect/cli/Command';
6
6
 
7
- import { create } from './create';
8
7
  import { credential } from './credential';
9
8
  import { identity } from './identity';
10
- // TODO(wittjosiah): Omitted currently due to p2p networking not working in bun.
11
- // import { join } from './join';
12
9
  import { keys } from './keys';
13
- import { recover } from './recover';
14
10
  import { seed } from './seed';
15
- // import { share } from './share';
16
11
  import { update } from './update';
17
12
 
13
+ // `create`, `join`, and `recover` are superseded by `dx account login`, which logs in to an
14
+ // existing identity via the same methods as Composer (email / atproto / device-invitation /
15
+ // recovery-code). Note: `join` was omitted and the `device-invitation` login method may hang —
16
+ // both rely on p2p networking which does not work in bun.
17
+
18
18
  export const halo: Command.Command<any, any, any, any> = Command.make('halo').pipe(
19
19
  Command.withDescription('Manage HALO identity.'),
20
- Command.withSubcommands([create, credential, identity, keys, recover, seed, update]),
20
+ Command.withSubcommands([credential, identity, keys, seed, update]),
21
21
  );
@@ -7,14 +7,13 @@ import * as Command from '@effect/cli/Command';
7
7
  import * as Prompt from '@effect/cli/Prompt';
8
8
  import * as Console from 'effect/Console';
9
9
  import * as Effect from 'effect/Effect';
10
- import * as Match from 'effect/Match';
11
10
 
12
11
  import { CommandConfig, print } from '@dxos/cli-util';
13
12
  import { type Client, ClientService } from '@dxos/client';
14
- import { type AuthenticatingInvitationObservable, Invitation, InvitationEncoder } from '@dxos/client/invitations';
13
+ import { Invitation, InvitationEncoder } from '@dxos/client/invitations';
15
14
  import { invariant } from '@dxos/invariant';
16
15
 
17
- import { printIdentity } from '../util';
16
+ import { printIdentity, waitForState } from '../util';
18
17
 
19
18
  export const join = Command.make(
20
19
  'join',
@@ -75,13 +74,3 @@ const sendInvitation = Effect.fn(function* ({
75
74
  yield* waitForState(invitation, Invitation.State.SUCCESS);
76
75
  return client.halo.identity.get();
77
76
  });
78
-
79
- const waitForState = (invitation: AuthenticatingInvitationObservable, state: Invitation.State) =>
80
- Effect.gen(function* () {
81
- const latch = yield* Effect.makeLatch();
82
- const subscription = invitation.subscribe((invitation) => {
83
- Match.value(invitation.state).pipe(Match.when(state, () => Effect.runSync(latch.open)));
84
- });
85
- yield* latch.await;
86
- subscription.unsubscribe();
87
- });
@@ -2,7 +2,11 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import * as Duration from 'effect/Duration';
6
+ import * as Effect from 'effect/Effect';
7
+
5
8
  import { FormBuilder } from '@dxos/cli-util';
9
+ import { type AuthenticatingInvitationObservable, Invitation } from '@dxos/client/invitations';
6
10
 
7
11
  /**
8
12
  * Pretty prints an identity with ANSI colors.
@@ -13,3 +17,23 @@ export const printIdentity = (identity: { identityDid: string; profile?: { displ
13
17
  FormBuilder.set('displayName', identity.profile?.displayName ?? '<none>'),
14
18
  FormBuilder.build,
15
19
  );
20
+
21
+ const TERMINAL_FAILURE_STATES = new Set([
22
+ Invitation.State.CANCELLED,
23
+ Invitation.State.TIMEOUT,
24
+ Invitation.State.ERROR,
25
+ Invitation.State.EXPIRED,
26
+ ]);
27
+
28
+ /** Await an authenticating invitation reaching the given state. */
29
+ export const waitForState = (invitation: AuthenticatingInvitationObservable, state: Invitation.State) =>
30
+ Effect.async<void, Error>((resume) => {
31
+ const subscription = invitation.subscribe((inv) => {
32
+ if (inv.state === state) {
33
+ resume(Effect.void);
34
+ } else if (TERMINAL_FAILURE_STATES.has(inv.state!)) {
35
+ resume(Effect.fail(new Error(`Invitation failed with state: ${Invitation.State[inv.state!]}`)));
36
+ }
37
+ });
38
+ return Effect.sync(() => subscription.unsubscribe());
39
+ }).pipe(Effect.timeout(Duration.minutes(3)));
@@ -2,6 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ export * from './account';
5
6
  export * from './config';
6
7
  export * from './device';
7
8
  export * from './edge';
package/src/constants.ts CHANGED
@@ -6,6 +6,6 @@ import { DXN } from '@dxos/keys';
6
6
 
7
7
  import { meta } from '#meta';
8
8
 
9
- export const JOIN_DIALOG = DXN.make(`${meta.id}.joinDialog`);
10
- export const RECOVERY_CODE_DIALOG = DXN.make(`${meta.id}.recoveryCodeDialog`);
11
- export const RESET_DIALOG = DXN.make(`${meta.id}.resetDialog`);
9
+ export const JOIN_DIALOG = DXN.make(`${meta.profile.key}.joinDialog`);
10
+ export const RECOVERY_CODE_DIALOG = DXN.make(`${meta.profile.key}.recoveryCodeDialog`);
11
+ export const RESET_DIALOG = DXN.make(`${meta.profile.key}.resetDialog`);
@@ -0,0 +1,46 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import * as Effect from 'effect/Effect';
7
+
8
+ import { ProcessManagerPlugin } from '@dxos/app-framework';
9
+ import { withPluginManager } from '@dxos/app-framework/testing';
10
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
+
12
+ import { initializeIdentity } from '#testing';
13
+ import { translations } from '#translations';
14
+
15
+ import { ClientPlugin } from '../../ClientPlugin';
16
+ import { AccountContainer } from './AccountContainer';
17
+
18
+ const meta = {
19
+ title: 'plugins/plugin-client/containers/AccountContainer',
20
+ component: AccountContainer,
21
+ decorators: [
22
+ withTheme(),
23
+ withLayout({ layout: 'fullscreen' }),
24
+ withPluginManager({
25
+ plugins: [
26
+ ClientPlugin({
27
+ onClientInitialized: ({ client }) =>
28
+ Effect.gen(function* () {
29
+ yield* initializeIdentity(client);
30
+ }),
31
+ }),
32
+ ProcessManagerPlugin(),
33
+ ],
34
+ }),
35
+ ],
36
+ parameters: {
37
+ layout: 'fullscreen',
38
+ translations,
39
+ },
40
+ } satisfies Meta<typeof AccountContainer>;
41
+
42
+ export default meta;
43
+
44
+ type Story = StoryObj<typeof meta>;
45
+
46
+ export const Default: Story = {};
@@ -11,18 +11,18 @@ import { Context } from '@dxos/context';
11
11
  import { createDidFromIdentityKey } from '@dxos/credentials';
12
12
  import { useIdentity } from '@dxos/react-client/halo';
13
13
  import { Button, Icon, IconButton, Input, Message, useAsyncEffect, useTranslation } from '@dxos/react-ui';
14
- import { Settings } from '@dxos/react-ui-form';
14
+ import { Form } from '@dxos/react-ui-form';
15
15
 
16
16
  import { meta } from '#meta';
17
17
  import { ClientCapabilities } from '#types';
18
18
 
19
19
  import { RESET_DIALOG } from '../../constants';
20
- import { useHubHttpClient } from '../../state/use-hub-http';
20
+ import { useHubHttpClient } from '../../hooks';
21
21
 
22
22
  type AccountState = 'loading' | 'present' | 'missing' | 'error';
23
23
 
24
24
  export const AccountContainer = () => {
25
- const { t } = useTranslation(meta.id);
25
+ const { t } = useTranslation(meta.profile.key);
26
26
  const identity = useIdentity();
27
27
  const { invokePromise } = useOperationInvoker();
28
28
  const accountCacheAtom = useCapability(ClientCapabilities.AccountCache);
@@ -114,66 +114,70 @@ export const AccountContainer = () => {
114
114
  const account = cache.account;
115
115
 
116
116
  return (
117
- <Settings.Viewport>
118
- <Settings.Section title={t('account-section.title')} description={t('account-section.description')}>
119
- {accountState === 'loading' ? null : accountState === 'missing' ? (
120
- <>
121
- <Message.Root valence='warning'>
122
- <Message.Title icon='ph--warning--duotone'>{t('no-edge-access.title')}</Message.Title>
123
- <Message.Content>{t('no-edge-access.description')}</Message.Content>
124
- </Message.Root>
125
- <Settings.Item title={t('request-access.label')} description={t('request-access.description')}>
126
- {requestSubmitted ? (
127
- <span className='text-sm text-description'>{t('access-request-submitted.message')}</span>
128
- ) : (
129
- <form onSubmit={handleRequestAccess} className='flex gap-2 items-center justify-end'>
130
- <Input.Root>
131
- <Input.TextInput
132
- type='email'
133
- required
134
- placeholder={t('access-request-email.placeholder')}
135
- value={requestEmail}
136
- onChange={(event) => setRequestEmail(event.target.value)}
137
- classNames='min-w-64'
138
- />
139
- </Input.Root>
140
- <Button type='submit' density='sm'>
141
- {t('request-access.label')}
117
+ <Form.Root variant='settings'>
118
+ <Form.Viewport scroll>
119
+ <Form.Content>
120
+ <Form.Section title={t('account-section.title')} description={t('account-section.description')}>
121
+ {accountState === 'loading' ? null : accountState === 'missing' ? (
122
+ <>
123
+ <Message.Root valence='warning'>
124
+ <Message.Title icon='ph--warning--duotone'>{t('no-edge-access.title')}</Message.Title>
125
+ <Message.Content>{t('no-edge-access.description')}</Message.Content>
126
+ </Message.Root>
127
+ <Form.Row label={t('request-access.label')} description={t('request-access.description')}>
128
+ {requestSubmitted ? (
129
+ <span className='text-sm text-description'>{t('access-request-submitted.message')}</span>
130
+ ) : (
131
+ <form onSubmit={handleRequestAccess} className='flex gap-2 items-center justify-end'>
132
+ <Input.Root>
133
+ <Input.TextInput
134
+ type='email'
135
+ required
136
+ placeholder={t('access-request-email.placeholder')}
137
+ value={requestEmail}
138
+ onChange={(event) => setRequestEmail(event.target.value)}
139
+ classNames='min-w-64'
140
+ />
141
+ </Input.Root>
142
+ <Button type='submit' density='sm'>
143
+ {t('request-access.label')}
144
+ </Button>
145
+ </form>
146
+ )}
147
+ </Form.Row>
148
+ </>
149
+ ) : accountState === 'error' && !account ? (
150
+ <Message.Root valence='error'>
151
+ <Message.Title icon='ph--cloud-x--duotone'>{t('account-offline.title')}</Message.Title>
152
+ <Message.Content>{t('account-offline.description')}</Message.Content>
153
+ </Message.Root>
154
+ ) : account ? (
155
+ <>
156
+ <Form.Row label={t('email.label')} description={account.email}>
157
+ {account.emailVerified ? (
158
+ <Icon icon='ph--check-circle--duotone' size={5} classNames='text-success-text justify-self-end' />
159
+ ) : (
160
+ <div className='flex flex-col gap-1 items-end'>
161
+ <IconButton
162
+ icon='ph--paper-plane-tilt--regular'
163
+ label={t('resend-verification.label')}
164
+ onClick={handleResend}
165
+ density='sm'
166
+ />
167
+ {resendStatus ? <span className='text-xs text-description'>{resendStatus}</span> : null}
168
+ </div>
169
+ )}
170
+ </Form.Row>
171
+ <Form.Row label={t('delete-account.label')} description={t('delete-account.description')}>
172
+ <Button variant='destructive' density='sm' onClick={handleDeleteAccount}>
173
+ {t('delete-account.label')}
142
174
  </Button>
143
- </form>
144
- )}
145
- </Settings.Item>
146
- </>
147
- ) : accountState === 'error' && !account ? (
148
- <Message.Root valence='error'>
149
- <Message.Title icon='ph--cloud-x--duotone'>{t('account-offline.title')}</Message.Title>
150
- <Message.Content>{t('account-offline.description')}</Message.Content>
151
- </Message.Root>
152
- ) : account ? (
153
- <>
154
- <Settings.Item title={t('email.label')} description={account.email}>
155
- {account.emailVerified ? (
156
- <Icon icon='ph--check-circle--duotone' size={5} classNames='text-success-text justify-self-end' />
157
- ) : (
158
- <div className='flex flex-col gap-1 items-end'>
159
- <IconButton
160
- icon='ph--paper-plane-tilt--regular'
161
- label={t('resend-verification.label')}
162
- onClick={handleResend}
163
- density='sm'
164
- />
165
- {resendStatus ? <span className='text-xs text-description'>{resendStatus}</span> : null}
166
- </div>
167
- )}
168
- </Settings.Item>
169
- <Settings.Item title={t('delete-account.label')} description={t('delete-account.description')}>
170
- <Button variant='destructive' density='sm' onClick={handleDeleteAccount}>
171
- {t('delete-account.label')}
172
- </Button>
173
- </Settings.Item>
174
- </>
175
- ) : null}
176
- </Settings.Section>
177
- </Settings.Viewport>
175
+ </Form.Row>
176
+ </>
177
+ ) : null}
178
+ </Form.Section>
179
+ </Form.Content>
180
+ </Form.Viewport>
181
+ </Form.Root>
178
182
  );
179
183
  };
@@ -11,8 +11,9 @@ import { useClient, useMulticastObservable } from '@dxos/react-client';
11
11
  import { type Device, useDevices } from '@dxos/react-client/halo';
12
12
  import { type CancellableInvitationObservable, Invitation, InvitationEncoder } from '@dxos/react-client/invitations';
13
13
  import { useNetworkStatus } from '@dxos/react-client/mesh';
14
- import { Button, Clipboard, Icon, IconButton, List, useId, useTranslation } from '@dxos/react-ui';
15
- import { Settings } from '@dxos/react-ui-form';
14
+ import { Button, Clipboard, Icon, IconButton, useId, useTranslation } from '@dxos/react-ui';
15
+ import { Form } from '@dxos/react-ui-form';
16
+ import { Listbox } from '@dxos/react-ui-list';
16
17
  import { AuthCode, Centered, DeviceListItem, Emoji, Viewport } from '@dxos/shell/react';
17
18
  import { osTranslations } from '@dxos/ui-theme';
18
19
  import { hexToEmoji } from '@dxos/util';
@@ -25,7 +26,7 @@ export type DevicesContainerProps = {
25
26
  };
26
27
 
27
28
  export const DevicesContainer = ({ createInvitationUrl }: DevicesContainerProps) => {
28
- const { t } = useTranslation(meta.id);
29
+ const { t } = useTranslation(meta.profile.key);
29
30
  const { invokePromise } = useOperationInvoker();
30
31
  const devices = useDevices();
31
32
  const { swarm: connectionState } = useNetworkStatus();
@@ -44,55 +45,72 @@ export const DevicesContainer = ({ createInvitationUrl }: DevicesContainerProps)
44
45
 
45
46
  return (
46
47
  <Clipboard.Provider>
47
- <Settings.Viewport>
48
- <Settings.Section
49
- title={t('devices-verbose.label', { ns: meta.id })}
50
- description={t('devices.description', { ns: meta.id })}
51
- >
52
- <Settings.Panel>
53
- <div role='group' className='min-w-0'>
54
- <h3 className='text-lg mb-2'>{t('devices.label', { ns: meta.id })}</h3>
55
- <List>
56
- {devices.map((device: Device) => (
57
- <DeviceListItem key={device.deviceKey.toHex()} device={device} connectionState={connectionState} />
58
- ))}
59
- </List>
60
- </div>
61
- {createInvitationUrl && (
62
- <div role='group' className='min-w-0'>
63
- <h3 className='text-lg mb-2'>{t('add-device.label')}</h3>
64
- <DeviceInvitation createInvitationUrl={createInvitationUrl} />
65
- </div>
66
- )}
67
- </Settings.Panel>
68
- </Settings.Section>
69
- <Settings.Section
70
- title={t('danger-zone.title', { ns: meta.id })}
71
- description={t('danger-zone.description', { ns: meta.id })}
72
- >
73
- <Settings.Item title={t('reset-device.label')} description={t('reset-device.description', { ns: meta.id })}>
74
- <Button variant='destructive' onClick={handleResetStorage} data-testid='devicesContainer.reset'>
75
- {t('reset-device.label')}
76
- </Button>
77
- </Settings.Item>
78
- <Settings.Item
79
- title={t('recover-identity.label')}
80
- description={t('recover-identity.description', { ns: meta.id })}
81
- >
82
- <Button variant='destructive' onClick={handleRecover} data-testid='devicesContainer.recover'>
83
- {t('recover-identity.label')}
84
- </Button>
85
- </Settings.Item>
86
- <Settings.Item
87
- title={t('join-new-identity.label')}
88
- description={t('join-new-identity.description', { ns: meta.id })}
89
- >
90
- <Button variant='destructive' onClick={handleJoinNewIdentity} data-testid='devicesContainer.joinExisting'>
91
- {t('join-new-identity.label')}
92
- </Button>
93
- </Settings.Item>
94
- </Settings.Section>
95
- </Settings.Viewport>
48
+ <Form.Root variant='settings'>
49
+ <Form.Viewport scroll>
50
+ <Form.Content>
51
+ <Form.Section
52
+ title={t('devices-verbose.label', { ns: meta.profile.key })}
53
+ description={t('devices.description', { ns: meta.profile.key })}
54
+ >
55
+ <Form.Group>
56
+ <div role='group' className='min-w-0'>
57
+ <h3 className='text-lg mb-2'>{t('devices.label', { ns: meta.profile.key })}</h3>
58
+ <Listbox.Root>
59
+ <Listbox.Content aria-label={t('devices.label', { ns: meta.profile.key })}>
60
+ {devices.map((device: Device) => (
61
+ <DeviceListItem
62
+ key={device.deviceKey.toHex()}
63
+ device={device}
64
+ connectionState={connectionState}
65
+ />
66
+ ))}
67
+ </Listbox.Content>
68
+ </Listbox.Root>
69
+ </div>
70
+ {createInvitationUrl && (
71
+ <div role='group' className='min-w-0'>
72
+ <h3 className='text-lg mb-2'>{t('add-device.label')}</h3>
73
+ <DeviceInvitation createInvitationUrl={createInvitationUrl} />
74
+ </div>
75
+ )}
76
+ </Form.Group>
77
+ </Form.Section>
78
+ <Form.Section
79
+ title={t('danger-zone.title', { ns: meta.profile.key })}
80
+ description={t('danger-zone.description', { ns: meta.profile.key })}
81
+ >
82
+ <Form.Row
83
+ label={t('reset-device.label')}
84
+ description={t('reset-device.description', { ns: meta.profile.key })}
85
+ >
86
+ <Button variant='destructive' onClick={handleResetStorage} data-testid='devicesContainer.reset'>
87
+ {t('reset-device.label')}
88
+ </Button>
89
+ </Form.Row>
90
+ <Form.Row
91
+ label={t('recover-identity.label')}
92
+ description={t('recover-identity.description', { ns: meta.profile.key })}
93
+ >
94
+ <Button variant='destructive' onClick={handleRecover} data-testid='devicesContainer.recover'>
95
+ {t('recover-identity.label')}
96
+ </Button>
97
+ </Form.Row>
98
+ <Form.Row
99
+ label={t('join-new-identity.label')}
100
+ description={t('join-new-identity.description', { ns: meta.profile.key })}
101
+ >
102
+ <Button
103
+ variant='destructive'
104
+ onClick={handleJoinNewIdentity}
105
+ data-testid='devicesContainer.joinExisting'
106
+ >
107
+ {t('join-new-identity.label')}
108
+ </Button>
109
+ </Form.Row>
110
+ </Form.Section>
111
+ </Form.Content>
112
+ </Form.Viewport>
113
+ </Form.Root>
96
114
  </Clipboard.Provider>
97
115
  );
98
116
  };
@@ -167,7 +185,7 @@ const InvitationSection = ({
167
185
  onInvitationDone = () => {},
168
186
  onInvitationCreate = () => {},
169
187
  }: InvitationComponentProps) => {
170
- const { t } = useTranslation(meta.id);
188
+ const { t } = useTranslation(meta.profile.key);
171
189
  const activeView =
172
190
  state < 0
173
191
  ? 'init'
@@ -215,7 +233,7 @@ const InvitationQR = ({ id, url, onCancel }: { id: string; url: string; onCancel
215
233
  const emoji = hexToEmoji(id);
216
234
  return (
217
235
  <>
218
- <p className='text-description'>{t('qr-code.description', { ns: meta.id })}</p>
236
+ <p className='text-description'>{t('qr-code.description', { ns: meta.profile.key })}</p>
219
237
  <div role='group' className='grid grid-cols-[1fr_min-content]'>
220
238
  <div className='flex justify-center py-4'>
221
239
  <div className='w-full md:max-w-80 aspect-square relative text-description'>
@@ -0,0 +1,46 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import * as Effect from 'effect/Effect';
7
+
8
+ import { ProcessManagerPlugin } from '@dxos/app-framework';
9
+ import { withPluginManager } from '@dxos/app-framework/testing';
10
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
+
12
+ import { initializeIdentity } from '#testing';
13
+ import { translations } from '#translations';
14
+
15
+ import { ClientPlugin } from '../../ClientPlugin';
16
+ import { InvitationsContainer } from './InvitationsContainer';
17
+
18
+ const meta = {
19
+ title: 'plugins/plugin-client/containers/InvitationsContainer',
20
+ component: InvitationsContainer,
21
+ decorators: [
22
+ withTheme(),
23
+ withLayout({ layout: 'fullscreen' }),
24
+ withPluginManager({
25
+ plugins: [
26
+ ClientPlugin({
27
+ onClientInitialized: ({ client }) =>
28
+ Effect.gen(function* () {
29
+ yield* initializeIdentity(client);
30
+ }),
31
+ }),
32
+ ProcessManagerPlugin(),
33
+ ],
34
+ }),
35
+ ],
36
+ parameters: {
37
+ layout: 'fullscreen',
38
+ translations,
39
+ },
40
+ } satisfies Meta<typeof InvitationsContainer>;
41
+
42
+ export default meta;
43
+
44
+ type Story = StoryObj<typeof meta>;
45
+
46
+ export const Default: Story = {};