@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
package/dx.config.ts ADDED
@@ -0,0 +1,19 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Config2 } from '@dxos/app-framework/config';
6
+ import { trim } from '@dxos/util';
7
+
8
+ export default Config2.make({
9
+ plugin: {
10
+ key: 'org.dxos.plugin.client',
11
+ name: 'Client',
12
+ author: 'DXOS',
13
+ description: trim`
14
+ Core client connectivity and peer-to-peer networking infrastructure.
15
+ Manages identity, authentication, and real-time synchronization across devices.
16
+ `,
17
+ tags: ['system'],
18
+ },
19
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-client",
3
- "version": "0.9.0",
3
+ "version": "0.9.1-staging.ee54ba693a",
4
4
  "description": "DXOS Surface plugin for DXOS Client",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -99,65 +99,68 @@
99
99
  "types": "dist/types/src/index.d.ts",
100
100
  "files": [
101
101
  "dist",
102
+ "dx.config.ts",
102
103
  "src"
103
104
  ],
104
105
  "dependencies": {
105
106
  "@effect/cli": "0.75.2",
106
- "@effect/platform": "0.96.1",
107
- "effect": "3.21.3",
107
+ "@effect/platform": "0.96.2",
108
+ "effect": "3.21.4",
108
109
  "react-qr-rounded": "^1.0.0",
109
110
  "yaml": "^2.8.2",
110
- "@dxos/app-framework": "0.9.0",
111
- "@dxos/app-toolkit": "0.9.0",
112
- "@dxos/async": "0.9.0",
113
- "@dxos/cli-util": "0.9.0",
114
- "@dxos/client-protocol": "0.9.0",
115
- "@dxos/compute": "0.9.0",
116
- "@dxos/client": "0.9.0",
117
- "@dxos/client-services": "0.9.0",
118
- "@dxos/config": "0.9.0",
119
- "@dxos/context": "0.9.0",
120
- "@dxos/echo": "0.9.0",
121
- "@dxos/echo-protocol": "0.9.0",
122
- "@dxos/edge-client": "0.9.0",
123
- "@dxos/credentials": "0.9.0",
124
- "@dxos/effect": "0.9.0",
125
- "@dxos/invariant": "0.9.0",
126
- "@dxos/functions": "0.9.0",
127
- "@dxos/keys": "0.9.0",
128
- "@dxos/plugin-graph": "0.9.0",
129
- "@dxos/log": "0.9.0",
130
- "@dxos/plugin-observability": "0.9.0",
131
- "@dxos/protocols": "0.9.0",
132
- "@dxos/react-client": "0.9.0",
133
- "@dxos/react-ui-form": "0.9.0",
134
- "@dxos/echo-client": "0.9.0",
135
- "@dxos/util": "0.9.0",
136
- "@dxos/react-ui-pickers": "0.9.0",
137
- "@dxos/shell": "0.9.0"
111
+ "@dxos/app-framework": "0.9.1-staging.ee54ba693a",
112
+ "@dxos/app-toolkit": "0.9.1-staging.ee54ba693a",
113
+ "@dxos/async": "0.9.1-staging.ee54ba693a",
114
+ "@dxos/cli-util": "0.9.1-staging.ee54ba693a",
115
+ "@dxos/client-protocol": "0.9.1-staging.ee54ba693a",
116
+ "@dxos/config": "0.9.1-staging.ee54ba693a",
117
+ "@dxos/client-services": "0.9.1-staging.ee54ba693a",
118
+ "@dxos/compute": "0.9.1-staging.ee54ba693a",
119
+ "@dxos/context": "0.9.1-staging.ee54ba693a",
120
+ "@dxos/credentials": "0.9.1-staging.ee54ba693a",
121
+ "@dxos/echo": "0.9.1-staging.ee54ba693a",
122
+ "@dxos/echo-client": "0.9.1-staging.ee54ba693a",
123
+ "@dxos/echo-protocol": "0.9.1-staging.ee54ba693a",
124
+ "@dxos/edge-client": "0.9.1-staging.ee54ba693a",
125
+ "@dxos/effect": "0.9.1-staging.ee54ba693a",
126
+ "@dxos/client": "0.9.1-staging.ee54ba693a",
127
+ "@dxos/invariant": "0.9.1-staging.ee54ba693a",
128
+ "@dxos/functions": "0.9.1-staging.ee54ba693a",
129
+ "@dxos/keys": "0.9.1-staging.ee54ba693a",
130
+ "@dxos/log": "0.9.1-staging.ee54ba693a",
131
+ "@dxos/plugin-graph": "0.9.1-staging.ee54ba693a",
132
+ "@dxos/plugin-observability": "0.9.1-staging.ee54ba693a",
133
+ "@dxos/protocols": "0.9.1-staging.ee54ba693a",
134
+ "@dxos/react-ui-form": "0.9.1-staging.ee54ba693a",
135
+ "@dxos/react-client": "0.9.1-staging.ee54ba693a",
136
+ "@dxos/react-ui-list": "0.9.1-staging.ee54ba693a",
137
+ "@dxos/react-ui-pickers": "0.9.1-staging.ee54ba693a",
138
+ "@dxos/react-ui-syntax-highlighter": "0.9.1-staging.ee54ba693a",
139
+ "@dxos/util": "0.9.1-staging.ee54ba693a",
140
+ "@dxos/shell": "0.9.1-staging.ee54ba693a"
138
141
  },
139
142
  "devDependencies": {
140
143
  "@effect-atom/atom-react": "^0.5.0",
141
144
  "@effect/printer": "0.49.0",
142
145
  "@effect/printer-ansi": "0.49.0",
143
- "@types/react": "~19.2.7",
146
+ "@types/react": "~19.2.17",
144
147
  "@types/react-dom": "~19.2.3",
145
- "react": "~19.2.3",
146
- "react-dom": "~19.2.3",
148
+ "react": "~19.2.7",
149
+ "react-dom": "~19.2.7",
147
150
  "vite": "^8.0.16",
148
- "@dxos/plugin-testing": "0.9.0",
149
- "@dxos/react-ui": "0.9.0",
150
- "@dxos/storybook-utils": "0.9.0",
151
- "@dxos/ui-theme": "0.9.0"
151
+ "@dxos/plugin-testing": "0.9.1-staging.ee54ba693a",
152
+ "@dxos/react-ui": "0.9.1-staging.ee54ba693a",
153
+ "@dxos/storybook-utils": "0.9.1-staging.ee54ba693a",
154
+ "@dxos/ui-theme": "0.9.1-staging.ee54ba693a"
152
155
  },
153
156
  "peerDependencies": {
154
157
  "@effect-atom/atom-react": "^0.5.0",
155
- "@effect/platform": "0.96.1",
156
- "effect": "3.21.3",
157
- "react": "~19.2.3",
158
- "react-dom": "~19.2.3",
159
- "@dxos/react-ui": "0.9.0",
160
- "@dxos/ui-theme": "0.9.0"
158
+ "@effect/platform": "0.96.2",
159
+ "effect": "3.21.4",
160
+ "react": "~19.2.7",
161
+ "react-dom": "~19.2.7",
162
+ "@dxos/react-ui": "0.9.1-staging.ee54ba693a",
163
+ "@dxos/ui-theme": "0.9.1-staging.ee54ba693a"
161
164
  },
162
165
  "publishConfig": {
163
166
  "access": "public"
@@ -10,11 +10,11 @@ import { meta } from '#meta';
10
10
  import { ClientEvents } from '#types';
11
11
  import { type ClientPluginOptions } from '#types';
12
12
 
13
- import { config, device, edge, halo, profile } from './commands';
13
+ import { account, config, device, edge, halo, profile } from './commands';
14
14
 
15
15
  export const ClientPlugin = Plugin.define<ClientPluginOptions>(meta).pipe(
16
16
  // TODO(wittjosiah): Could some of these commands make use of operations?
17
- AppPlugin.addCommandModule({ commands: [config, device, edge, halo, profile] }),
17
+ AppPlugin.addCommandModule({ commands: [account, config, device, edge, halo, profile] }),
18
18
  AppPlugin.addOperationHandlerModule({ activate: OperationHandler }),
19
19
  Plugin.addModule((options) => ({
20
20
  id: Capability.getModuleTag(Client),
@@ -11,7 +11,7 @@ import { createComposerTestApp } from '@dxos/plugin-testing/harness';
11
11
 
12
12
  import { meta } from './meta';
13
13
 
14
- const moduleId = (name: string) => `${meta.id}.module.${name}`;
14
+ const moduleId = (name: string) => `${meta.profile.key}.module.${name}`;
15
15
 
16
16
  describe('ClientPlugin', () => {
17
17
  test('modules activate on the expected events', async ({ expect }) => {
@@ -7,9 +7,7 @@ import * as Effect from 'effect/Effect';
7
7
  import { Capability } from '@dxos/app-framework';
8
8
  import { createKvsStore } from '@dxos/effect';
9
9
 
10
- import { ClientCapabilities } from '#types';
11
-
12
- import { AccountCache } from '../state/account-cache';
10
+ import { AccountCache, ClientCapabilities } from '#types';
13
11
 
14
12
  export default Capability.makeModule(() =>
15
13
  Effect.succeed(
@@ -25,7 +25,7 @@ export default Capability.makeModule(
25
25
  id: 'openUserAccount',
26
26
  data: () => Operation.invoke(ClientOperation.ShareIdentity),
27
27
  properties: {
28
- label: ['open-user-account.label', { ns: meta.id }],
28
+ label: ['open-user-account.label', { ns: meta.profile.key }],
29
29
  icon: 'ph--user--regular',
30
30
  disposition: 'menu',
31
31
  keyBinding: {
@@ -37,77 +37,87 @@ export default Capability.makeModule(
37
37
  },
38
38
  },
39
39
  ]),
40
- connector: Effect.fnUntraced(function* (node, get) {
41
- const client = yield* Capability.get(ClientCapabilities.Client);
42
- const identity = get(CreateAtom.fromObservable(client.halo.identity));
43
- const status = get(CreateAtom.fromObservable(client.mesh.networkStatus));
40
+ connector: (node, get) =>
41
+ Effect.gen(function* () {
42
+ const client = yield* Capability.get(ClientCapabilities.Client);
43
+ const identity = get(CreateAtom.fromObservable(client.halo.identity));
44
+ const status = get(CreateAtom.fromObservable(client.mesh.networkStatus));
44
45
 
45
- return [
46
- Node.make({
47
- id: Account.id,
48
- type: meta.id,
49
- properties: {
50
- label: ['account.label', { ns: meta.id }],
51
- icon: 'ph--user--regular',
52
- disposition: 'user-account',
53
- testId: 'clientPlugin.account',
54
- // NOTE: This currently needs to be the identity key because the fallback is generated from hex.
55
- userId: identity?.identityKey.toHex(),
56
- hue: identity?.profile?.data?.hue,
57
- emoji: identity?.profile?.data?.emoji,
58
- status: status.swarm === ConnectionState.OFFLINE ? 'error' : 'active',
59
- },
60
- nodes: [
61
- Node.make({
62
- id: Account.Profile,
63
- data: Account.Profile,
64
- type: meta.id,
65
- properties: {
66
- label: ['profile.label', { ns: meta.id }],
67
- icon: 'ph--user--regular',
68
- },
69
- }),
70
- Node.make({
71
- id: Account.Account,
72
- data: Account.Account,
73
- type: meta.id,
74
- properties: {
75
- label: ['account-panel.label', { ns: meta.id }],
76
- icon: 'ph--identification-card--regular',
77
- },
78
- }),
79
- Node.make({
80
- id: Account.Devices,
81
- data: Account.Devices,
82
- type: meta.id,
83
- properties: {
84
- label: ['devices.label', { ns: meta.id }],
85
- icon: 'ph--devices--regular',
86
- testId: 'clientPlugin.devices',
87
- },
88
- }),
89
- Node.make({
90
- id: Account.Security,
91
- data: Account.Security,
92
- type: meta.id,
93
- properties: {
94
- label: ['security.label', { ns: meta.id }],
95
- icon: 'ph--key--regular',
96
- },
97
- }),
98
- Node.make({
99
- id: Account.Invitations,
100
- data: Account.Invitations,
101
- type: meta.id,
102
- properties: {
103
- label: ['invitations-panel.label', { ns: meta.id }],
104
- icon: 'ph--ticket--regular',
105
- },
106
- }),
107
- ],
108
- }),
109
- ];
110
- }),
46
+ return [
47
+ Node.make({
48
+ id: Account.id,
49
+ type: meta.profile.key,
50
+ properties: {
51
+ label: ['account.label', { ns: meta.profile.key }],
52
+ icon: 'ph--user--regular',
53
+ disposition: 'user-account',
54
+ testId: 'clientPlugin.account',
55
+ // NOTE: This currently needs to be the identity key because the fallback is generated from hex.
56
+ userId: identity?.identityKey.toHex(),
57
+ hue: identity?.profile?.data?.hue,
58
+ emoji: identity?.profile?.data?.emoji,
59
+ status: status.swarm === ConnectionState.OFFLINE ? 'error' : 'active',
60
+ },
61
+ nodes: [
62
+ Node.make({
63
+ id: Account.Profile,
64
+ data: Account.Profile,
65
+ type: meta.profile.key,
66
+ properties: {
67
+ label: ['profile.label', { ns: meta.profile.key }],
68
+ icon: 'ph--user--regular',
69
+ },
70
+ }),
71
+ Node.make({
72
+ id: Account.Account,
73
+ data: Account.Account,
74
+ type: meta.profile.key,
75
+ properties: {
76
+ label: ['account-panel.label', { ns: meta.profile.key }],
77
+ icon: 'ph--identification-card--regular',
78
+ },
79
+ }),
80
+ Node.make({
81
+ id: Account.Devices,
82
+ data: Account.Devices,
83
+ type: meta.profile.key,
84
+ properties: {
85
+ label: ['devices.label', { ns: meta.profile.key }],
86
+ icon: 'ph--devices--regular',
87
+ testId: 'clientPlugin.devices',
88
+ },
89
+ }),
90
+ Node.make({
91
+ id: Account.Security,
92
+ data: Account.Security,
93
+ type: meta.profile.key,
94
+ properties: {
95
+ label: ['security.label', { ns: meta.profile.key }],
96
+ icon: 'ph--key--regular',
97
+ },
98
+ }),
99
+ Node.make({
100
+ id: Account.Invitations,
101
+ data: Account.Invitations,
102
+ type: meta.profile.key,
103
+ properties: {
104
+ label: ['invitations-panel.label', { ns: meta.profile.key }],
105
+ icon: 'ph--ticket--regular',
106
+ },
107
+ }),
108
+ Node.make({
109
+ id: Account.Usage,
110
+ data: Account.Usage,
111
+ type: meta.profile.key,
112
+ properties: {
113
+ label: ['usage-panel.label', { ns: meta.profile.key }],
114
+ icon: 'ph--chart-bar--regular',
115
+ },
116
+ }),
117
+ ],
118
+ }),
119
+ ];
120
+ }).pipe(Effect.orDie),
111
121
  });
112
122
 
113
123
  return Capability.contributes(AppCapabilities.AppGraphBuilder, extensions);
@@ -8,8 +8,7 @@ import * as Layer from 'effect/Layer';
8
8
  import { Capabilities, Capability } from '@dxos/app-framework';
9
9
  import { ClientService } from '@dxos/client';
10
10
  import { Credential, LayerSpec } from '@dxos/compute';
11
- import { Database, Feed } from '@dxos/echo';
12
- import { createFeedServiceLayer } from '@dxos/echo-client';
11
+ import { Database } from '@dxos/echo';
13
12
  import { credentialsLayerFromDatabase } from '@dxos/functions';
14
13
  import { invariant } from '@dxos/invariant';
15
14
 
@@ -20,8 +19,7 @@ import { ClientCapabilities } from '#types';
20
19
  //
21
20
  // Contributes the core client/space service layer specs:
22
21
  // - {@link ClientService} (application affinity).
23
- // - {@link Database.Service}, {@link Feed.FeedService},
24
- // {@link Credential.CredentialsService} (space affinity).
22
+ // - {@link Database.Service}, {@link Credential.CredentialsService} (space affinity).
25
23
  //
26
24
  // Specs are declared at module level and resolve the underlying
27
25
  // {@link ClientCapabilities.Client} through the Effect layer graph (via
@@ -59,7 +57,7 @@ const DatabaseLayerSpec = LayerSpec.make(
59
57
  {
60
58
  affinity: 'space',
61
59
  requires: [ClientService],
62
- provides: [Database.Service, Feed.FeedService],
60
+ provides: [Database.Service],
63
61
  },
64
62
  (context) =>
65
63
  Layer.unwrapEffect(
@@ -69,7 +67,7 @@ const DatabaseLayerSpec = LayerSpec.make(
69
67
  const space = client.spaces.get(context.space);
70
68
  invariant(space, `space not found on client: ${context.space}`);
71
69
  yield* Effect.promise(() => space.waitUntilReady());
72
- return Layer.mergeAll(Database.layer(space.db), createFeedServiceLayer(space.queues));
70
+ return Database.layer(space.db);
73
71
  }),
74
72
  ),
75
73
  );
@@ -15,7 +15,7 @@ import { ClientCapabilities } from '#types';
15
15
  export default Capability.makeModule(() =>
16
16
  Effect.succeed(
17
17
  Capability.contributes(Capabilities.ReactContext, {
18
- id: meta.id,
18
+ id: meta.profile.key,
19
19
  context: ({ children }: { children?: ReactNode }) => {
20
20
  const client = useCapability(ClientCapabilities.Client);
21
21
  return <ClientProvider client={client}>{children}</ClientProvider>;
@@ -18,6 +18,7 @@ import {
18
18
  RecoveryCodeDialog,
19
19
  RecoveryCredentialsContainer,
20
20
  ResetDialog,
21
+ UsageContainer,
21
22
  } from '#containers';
22
23
  import { Account, type ClientPluginOptions } from '#types';
23
24
 
@@ -57,6 +58,11 @@ export default Capability.makeModule(
57
58
  filter: AppSurface.literal(AppSurface.Article, Account.Invitations),
58
59
  component: () => <InvitationsContainer />,
59
60
  }),
61
+ Surface.create({
62
+ id: Account.Usage,
63
+ filter: AppSurface.literal(AppSurface.Article, Account.Usage),
64
+ component: () => <UsageContainer />,
65
+ }),
60
66
  Surface.create({
61
67
  id: JOIN_DIALOG,
62
68
  filter: AppSurface.component<ComponentProps<typeof JoinDialog>>(AppSurface.Dialog, JOIN_DIALOG),
@@ -0,0 +1,13 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Command from '@effect/cli/Command';
6
+
7
+ import { login } from './login';
8
+ import { logout } from './logout';
9
+
10
+ export const account: Command.Command<any, any, any, any> = Command.make('account').pipe(
11
+ Command.withDescription('Log in and out of a DXOS identity.'),
12
+ Command.withSubcommands([login, logout]),
13
+ );
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export * from './login';
@@ -0,0 +1,147 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Args from '@effect/cli/Args';
6
+ import * as Command from '@effect/cli/Command';
7
+ import * as Options from '@effect/cli/Options';
8
+ import * as Prompt from '@effect/cli/Prompt';
9
+ import * as Console from 'effect/Console';
10
+ import * as Effect from 'effect/Effect';
11
+ import * as Match from 'effect/Match';
12
+ import * as Option from 'effect/Option';
13
+
14
+ import { CommandConfig, performRecoveryOAuthFlow, print } from '@dxos/cli-util';
15
+ import { type Client, ClientService } from '@dxos/client';
16
+ import { Invitation, InvitationEncoder } from '@dxos/client/invitations';
17
+ import { Context as DxContext } from '@dxos/context';
18
+ import { HubHttpClient } from '@dxos/edge-client';
19
+ import { invariant } from '@dxos/invariant';
20
+ import { ATPROTO_OAUTH_SCOPES, OAuthProvider } from '@dxos/protocols';
21
+
22
+ import { printIdentity, waitForState } from '../../halo/util';
23
+
24
+ type LoginMethod = 'email' | 'atproto' | 'device-invitation' | 'recovery-code';
25
+
26
+ const LOGIN_METHODS: LoginMethod[] = ['email', 'atproto', 'device-invitation', 'recovery-code'];
27
+
28
+ const METHOD_CHOICES = [
29
+ { title: 'Email', value: 'email' as const },
30
+ { title: 'AT Protocol', value: 'atproto' as const },
31
+ { title: 'Device invitation', value: 'device-invitation' as const },
32
+ { title: 'Recovery code', value: 'recovery-code' as const },
33
+ ];
34
+
35
+ const INPUT_PROMPT: Record<LoginMethod, string> = {
36
+ 'email': 'Email address',
37
+ 'atproto': 'atproto handle or DID (e.g. alice.bsky.social)',
38
+ 'device-invitation': 'Invitation code or URL',
39
+ 'recovery-code': 'Recovery code (seed phrase)',
40
+ };
41
+
42
+ export const login = Command.make(
43
+ 'login',
44
+ {
45
+ method: Options.choice('method', LOGIN_METHODS).pipe(
46
+ Options.withDescription(
47
+ 'Login method (email | atproto | device-invitation | recovery-code). Prompted if omitted.',
48
+ ),
49
+ Options.optional,
50
+ ),
51
+ input: Args.text({ name: 'input' }).pipe(
52
+ Args.withDescription('Method input: email address / atproto handle / invitation code / recovery code.'),
53
+ Args.optional,
54
+ ),
55
+ },
56
+ Effect.fn(function* ({ method, input }) {
57
+ const { json } = yield* CommandConfig;
58
+ const client = yield* ClientService;
59
+ // TODO(wittjosiah): How to surface this error to the user cleanly?
60
+ invariant(!client.halo.identity.get(), 'Already logged in. Run `dx account logout` first.');
61
+
62
+ const resolvedMethod: LoginMethod = Option.isSome(method)
63
+ ? method.value
64
+ : yield* Prompt.select({ message: 'Choose a login method:', choices: METHOD_CHOICES }).pipe(Prompt.run);
65
+
66
+ const resolvedInput = Option.isSome(input)
67
+ ? input.value
68
+ : yield* Prompt.text({ message: `${INPUT_PROMPT[resolvedMethod]}:` }).pipe(Prompt.run);
69
+
70
+ const identity = yield* Match.value(resolvedMethod).pipe(
71
+ Match.when('atproto', () => loginWithAtproto(client, resolvedInput)),
72
+ Match.when('email', () => loginWithEmail(client, resolvedInput)),
73
+ Match.when('recovery-code', () => loginWithRecoveryCode(client, resolvedInput)),
74
+ Match.when('device-invitation', () => loginWithDeviceInvitation(client, resolvedInput)),
75
+ Match.exhaustive,
76
+ );
77
+
78
+ if (json) {
79
+ yield* Console.log(
80
+ JSON.stringify({ identityDid: identity.did, displayName: identity.profile?.displayName }, null, 2),
81
+ );
82
+ } else {
83
+ yield* Console.log('Logged in successfully');
84
+ yield* Console.log(print(printIdentity({ identityDid: identity.did, profile: identity.profile })));
85
+ }
86
+ }),
87
+ ).pipe(Command.withDescription('Log in to an existing DXOS identity (same methods as Composer).'));
88
+
89
+ /**
90
+ * atproto / Bluesky OAuth login: runs the gate recovery flow (local server + browser) and redeems
91
+ * the resulting one-time `recoveryProof` to admit this device into the existing identity's HALO.
92
+ */
93
+ const loginWithAtproto = (client: Client, handle: string) =>
94
+ Effect.gen(function* () {
95
+ const edgeBaseUrl = client.config.values.runtime?.services?.edge?.url;
96
+ invariant(edgeBaseUrl, 'Edge services not configured (runtime.services.edge.url).');
97
+ const { recoveryProof } = yield* performRecoveryOAuthFlow({
98
+ edgeBaseUrl,
99
+ provider: OAuthProvider.ATPROTO,
100
+ scopes: ATPROTO_OAUTH_SCOPES,
101
+ loginHint: handle,
102
+ });
103
+ return yield* Effect.tryPromise(() => client.halo.recoverIdentity({ recoveryProof }));
104
+ });
105
+
106
+ /** Recovery-code (seed phrase) login. */
107
+ const loginWithRecoveryCode = (client: Client, recoveryCode: string) =>
108
+ Effect.tryPromise(() => client.halo.recoverIdentity({ recoveryCode }));
109
+
110
+ /**
111
+ * Email login: hub-service inlines a one-time `token` for test emails and emails it out-of-band
112
+ * otherwise. Redeems the token to admit this device into HALO.
113
+ */
114
+ const loginWithEmail = (client: Client, email: string) =>
115
+ Effect.gen(function* () {
116
+ const hubUrl = client.config.values?.runtime?.app?.env?.DX_HUB_URL;
117
+ invariant(hubUrl, 'Hub URL not configured (runtime.app.env.DX_HUB_URL).');
118
+ const hub = new HubHttpClient(hubUrl);
119
+ const { token: inlineToken } = yield* Effect.tryPromise(() => hub.login(DxContext.default(), { email }));
120
+ let token = inlineToken;
121
+ if (!token) {
122
+ yield* Console.log(`A login link was sent to ${email}. Paste the token from the email below.`);
123
+ token = yield* Prompt.text({ message: 'Login token' }).pipe(Prompt.run);
124
+ }
125
+ return yield* Effect.tryPromise(() => client.halo.recoverIdentity({ token }));
126
+ });
127
+
128
+ /**
129
+ * Device-invitation login: joins an existing identity from another authorized device.
130
+ *
131
+ * NOTE: p2p networking does not work in bun — this method will likely hang waiting for the peer.
132
+ */
133
+ const loginWithDeviceInvitation = (client: Client, encoded: string) =>
134
+ Effect.gen(function* () {
135
+ let code = encoded;
136
+ if (code.startsWith('http') || code.startsWith('socket')) {
137
+ code = new URL(code).searchParams.get('deviceInvitationCode') ?? code;
138
+ }
139
+ const invitation = client.halo.join(InvitationEncoder.decode(code));
140
+ yield* waitForState(invitation, Invitation.State.READY_FOR_AUTHENTICATION);
141
+ const authCode = yield* Prompt.text({ message: 'Enter the authentication code' }).pipe(Prompt.run);
142
+ yield* Effect.tryPromise(() => invitation.authenticate(authCode));
143
+ yield* waitForState(invitation, Invitation.State.SUCCESS);
144
+ const identity = client.halo.identity.get();
145
+ invariant(identity, 'Device invitation completed but no identity is present.');
146
+ return identity;
147
+ });
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export * from './logout';
@@ -0,0 +1,49 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Command from '@effect/cli/Command';
6
+ import * as Options from '@effect/cli/Options';
7
+ import * as Prompt from '@effect/cli/Prompt';
8
+ import * as Error from '@effect/platform/Error';
9
+ import * as FileSystem from '@effect/platform/FileSystem';
10
+ import * as Console from 'effect/Console';
11
+ import * as Effect from 'effect/Effect';
12
+
13
+ import { CommandConfig } from '@dxos/cli-util';
14
+ import { DX_DATA, getProfilePath } from '@dxos/client-protocol';
15
+ import { ConfigService } from '@dxos/config';
16
+
17
+ export const logout = Command.make(
18
+ 'logout',
19
+ {
20
+ force: Options.boolean('force', { ifPresent: true }).pipe(Options.withDescription('Skip confirmation prompt.')),
21
+ },
22
+ Effect.fnUntraced(function* ({ force }) {
23
+ const fs = yield* FileSystem.FileSystem;
24
+ const config = yield* ConfigService;
25
+ const { json, profile } = yield* CommandConfig;
26
+ const path = config.values.runtime?.client?.storage?.dataRoot ?? getProfilePath(DX_DATA, profile);
27
+ if (!force) {
28
+ const confirmed = yield* Prompt.confirm({
29
+ message: `Log out of profile (${profile})? This removes the local identity and data; spaces re-sync on next login.`,
30
+ initial: false,
31
+ }).pipe(Prompt.run);
32
+ if (!confirmed) {
33
+ return;
34
+ }
35
+ }
36
+
37
+ yield* fs.remove(path, { recursive: true }).pipe(
38
+ Effect.catchIf(
39
+ (e): e is Error.SystemError => e._tag === 'SystemError' && e.reason === 'NotFound',
40
+ () => Effect.void,
41
+ ),
42
+ );
43
+ if (json) {
44
+ yield* Console.log(JSON.stringify({ profile, loggedOut: true }, null, 2));
45
+ } else {
46
+ yield* Console.log(`Logged out of profile (${profile}).`);
47
+ }
48
+ }),
49
+ ).pipe(Command.withDescription('Log out of the current profile (clears the local identity and data).'));