@dxos/shell 0.6.13-staging.1e988a3 → 0.6.14-main.2b6a0f3

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 (124) hide show
  1. package/dist/bundle/shell.mjs +12070 -15401
  2. package/dist/bundle/shell.mjs.map +1 -1
  3. package/dist/bundle/shell.umd.js +87 -94
  4. package/dist/bundle/shell.umd.js.map +1 -1
  5. package/dist/bundle/style.css +1 -1
  6. package/dist/lib/browser/meta.json +1 -1
  7. package/dist/lib/browser/react.mjs +551 -453
  8. package/dist/lib/browser/react.mjs.map +4 -4
  9. package/dist/lib/browser/testing/index.mjs +2 -7
  10. package/dist/lib/browser/testing/index.mjs.map +3 -3
  11. package/dist/types/src/components/CompoundButton/CompoundButton.stories.js +1 -1
  12. package/dist/types/src/components/CompoundButton/CompoundButton.stories.js.map +1 -1
  13. package/dist/types/src/components/IdentityList/SpaceMemberList.stories.d.ts +0 -3
  14. package/dist/types/src/components/IdentityList/SpaceMemberList.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/IdentityList/SpaceMemberList.stories.js +1 -2
  16. package/dist/types/src/components/IdentityList/SpaceMemberList.stories.js.map +1 -1
  17. package/dist/types/src/components/InvitationList/InvitationList.stories.d.ts +0 -3
  18. package/dist/types/src/components/InvitationList/InvitationList.stories.d.ts.map +1 -1
  19. package/dist/types/src/components/InvitationList/InvitationList.stories.js +1 -2
  20. package/dist/types/src/components/InvitationList/InvitationList.stories.js.map +1 -1
  21. package/dist/types/src/components/Panel/Panel.stories.d.ts +0 -3
  22. package/dist/types/src/components/Panel/Panel.stories.d.ts.map +1 -1
  23. package/dist/types/src/components/Panel/Panel.stories.js +1 -2
  24. package/dist/types/src/components/Panel/Panel.stories.js.map +1 -1
  25. package/dist/types/src/components/Viewport/Viewport.stories.js +1 -1
  26. package/dist/types/src/components/Viewport/Viewport.stories.js.map +1 -1
  27. package/dist/types/src/composites/Shell/Shell.d.ts.map +1 -1
  28. package/dist/types/src/composites/Shell/Shell.js +4 -3
  29. package/dist/types/src/composites/Shell/Shell.js.map +1 -1
  30. package/dist/types/src/composites/Shell/run-shell.js +1 -1
  31. package/dist/types/src/composites/Shell/run-shell.js.map +1 -1
  32. package/dist/types/src/panels/IdentityPanel/IdentityPanel.js +1 -1
  33. package/dist/types/src/panels/IdentityPanel/IdentityPanel.js.map +1 -1
  34. package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.d.ts.map +1 -1
  35. package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.js +7 -2
  36. package/dist/types/src/panels/IdentityPanel/IdentityPanel.stories.js.map +1 -1
  37. package/dist/types/src/panels/IdentityPanel/IdentityPanelProps.d.ts +4 -4
  38. package/dist/types/src/panels/IdentityPanel/IdentityPanelProps.d.ts.map +1 -1
  39. package/dist/types/src/panels/IdentityPanel/useAgentHandlers.d.ts +1 -1
  40. package/dist/types/src/panels/IdentityPanel/useAgentHandlers.d.ts.map +1 -1
  41. package/dist/types/src/panels/IdentityPanel/useAgentHandlers.js +10 -108
  42. package/dist/types/src/panels/IdentityPanel/useAgentHandlers.js.map +1 -1
  43. package/dist/types/src/panels/IdentityPanel/useEdgeAgentsHandlers.d.ts +8 -0
  44. package/dist/types/src/panels/IdentityPanel/useEdgeAgentsHandlers.d.ts.map +1 -0
  45. package/dist/types/src/panels/IdentityPanel/useEdgeAgentsHandlers.js +84 -0
  46. package/dist/types/src/panels/IdentityPanel/useEdgeAgentsHandlers.js.map +1 -0
  47. package/dist/types/src/panels/IdentityPanel/useKubeAgentHandlers.d.ts +8 -0
  48. package/dist/types/src/panels/IdentityPanel/useKubeAgentHandlers.d.ts.map +1 -0
  49. package/dist/types/src/panels/IdentityPanel/useKubeAgentHandlers.js +119 -0
  50. package/dist/types/src/panels/IdentityPanel/useKubeAgentHandlers.js.map +1 -0
  51. package/dist/types/src/panels/JoinPanel/JoinPanel.d.ts.map +1 -1
  52. package/dist/types/src/panels/JoinPanel/JoinPanel.js +24 -16
  53. package/dist/types/src/panels/JoinPanel/JoinPanel.js.map +1 -1
  54. package/dist/types/src/panels/JoinPanel/JoinPanel.stories.d.ts +3 -0
  55. package/dist/types/src/panels/JoinPanel/JoinPanel.stories.d.ts.map +1 -1
  56. package/dist/types/src/panels/JoinPanel/JoinPanel.stories.js +3 -2
  57. package/dist/types/src/panels/JoinPanel/JoinPanel.stories.js.map +1 -1
  58. package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts +7 -3
  59. package/dist/types/src/panels/JoinPanel/JoinPanelProps.d.ts.map +1 -1
  60. package/dist/types/src/panels/JoinPanel/joinMachine.d.ts.map +1 -1
  61. package/dist/types/src/panels/JoinPanel/joinMachine.js +2 -0
  62. package/dist/types/src/panels/JoinPanel/joinMachine.js.map +1 -1
  63. package/dist/types/src/panels/JoinPanel/steps/InvitationAuthenticator.d.ts +3 -1
  64. package/dist/types/src/panels/JoinPanel/steps/InvitationAuthenticator.d.ts.map +1 -1
  65. package/dist/types/src/panels/JoinPanel/steps/InvitationAuthenticator.js +9 -6
  66. package/dist/types/src/panels/JoinPanel/steps/InvitationAuthenticator.js.map +1 -1
  67. package/dist/types/src/panels/JoinPanel/steps/InvitationInput.d.ts.map +1 -1
  68. package/dist/types/src/panels/JoinPanel/steps/InvitationInput.js +8 -2
  69. package/dist/types/src/panels/JoinPanel/steps/InvitationInput.js.map +1 -1
  70. package/dist/types/src/panels/Panels.stories.js +1 -1
  71. package/dist/types/src/panels/Panels.stories.js.map +1 -1
  72. package/dist/types/src/panels/SpacePanel/SpacePanel.js +1 -1
  73. package/dist/types/src/panels/SpacePanel/SpacePanel.js.map +1 -1
  74. package/dist/types/src/panels/SpacePanel/SpacePanel.stories.js +1 -1
  75. package/dist/types/src/panels/SpacePanel/SpacePanel.stories.js.map +1 -1
  76. package/dist/types/src/panels/SpacePanel/SpacePanelProps.d.ts +4 -4
  77. package/dist/types/src/panels/SpacePanel/SpacePanelProps.d.ts.map +1 -1
  78. package/dist/types/src/playwright/invitations-manager.d.ts +1 -1
  79. package/dist/types/src/playwright/invitations-manager.d.ts.map +1 -1
  80. package/dist/types/src/playwright/invitations-manager.js +4 -10
  81. package/dist/types/src/playwright/invitations-manager.js.map +1 -1
  82. package/dist/types/src/playwright/invitations.spec.js +31 -32
  83. package/dist/types/src/playwright/invitations.spec.js.map +1 -1
  84. package/dist/types/src/playwright/playwright.config.d.ts +2 -2
  85. package/dist/types/src/playwright/playwright.config.d.ts.map +1 -1
  86. package/dist/types/src/playwright/playwright.config.js +12 -2
  87. package/dist/types/src/playwright/playwright.config.js.map +1 -1
  88. package/dist/types/src/testing/shell-manager.d.ts +1 -0
  89. package/dist/types/src/testing/shell-manager.d.ts.map +1 -1
  90. package/dist/types/src/testing/shell-manager.js +2 -7
  91. package/dist/types/src/testing/shell-manager.js.map +1 -1
  92. package/dist/types/src/translations/locales/en-US.d.ts +1 -0
  93. package/dist/types/src/translations/locales/en-US.d.ts.map +1 -1
  94. package/dist/types/src/translations/locales/en-US.js +4 -3
  95. package/dist/types/src/translations/locales/en-US.js.map +1 -1
  96. package/package.json +29 -27
  97. package/src/components/CompoundButton/CompoundButton.stories.tsx +1 -1
  98. package/src/components/IdentityList/SpaceMemberList.stories.tsx +1 -2
  99. package/src/components/InvitationList/InvitationList.stories.tsx +1 -2
  100. package/src/components/Panel/Panel.stories.tsx +1 -2
  101. package/src/components/Viewport/Viewport.stories.tsx +1 -1
  102. package/src/composites/Shell/Shell.tsx +4 -3
  103. package/src/composites/Shell/run-shell.tsx +1 -1
  104. package/src/panels/IdentityPanel/IdentityPanel.stories.tsx +7 -2
  105. package/src/panels/IdentityPanel/IdentityPanel.tsx +3 -3
  106. package/src/panels/IdentityPanel/IdentityPanelProps.ts +4 -4
  107. package/src/panels/IdentityPanel/useAgentHandlers.ts +9 -114
  108. package/src/panels/IdentityPanel/useEdgeAgentsHandlers.ts +96 -0
  109. package/src/panels/IdentityPanel/useKubeAgentHandlers.ts +128 -0
  110. package/src/panels/JoinPanel/JoinPanel.stories.tsx +3 -2
  111. package/src/panels/JoinPanel/JoinPanel.tsx +31 -9
  112. package/src/panels/JoinPanel/JoinPanelProps.ts +7 -3
  113. package/src/panels/JoinPanel/joinMachine.ts +2 -0
  114. package/src/panels/JoinPanel/steps/InvitationAuthenticator.tsx +29 -16
  115. package/src/panels/JoinPanel/steps/InvitationInput.tsx +9 -17
  116. package/src/panels/Panels.stories.tsx +1 -1
  117. package/src/panels/SpacePanel/SpacePanel.stories.tsx +1 -1
  118. package/src/panels/SpacePanel/SpacePanel.tsx +3 -3
  119. package/src/panels/SpacePanel/SpacePanelProps.ts +4 -4
  120. package/src/playwright/invitations-manager.ts +4 -8
  121. package/src/playwright/invitations.spec.ts +32 -33
  122. package/src/playwright/playwright.config.ts +13 -2
  123. package/src/testing/shell-manager.ts +4 -9
  124. package/src/translations/locales/en-US.ts +4 -3
@@ -13,10 +13,9 @@ import { InvitationList } from './InvitationList';
13
13
  import { inviteWithState } from '../../testing';
14
14
 
15
15
  export default {
16
- title: 'react-shell/InvitationList',
16
+ title: 'sdk/react-shell/InvitationList',
17
17
  component: InvitationList,
18
18
  decorators: [withTheme, withLayout({ tooltips: true })],
19
- actions: { argTypesRegex: '^on.*' },
20
19
  parameters: { chromatic: { disableSnapshot: false } },
21
20
  };
22
21
 
@@ -15,9 +15,8 @@ import { Label } from './Label';
15
15
  import { StorybookDialog } from '../StorybookDialog';
16
16
 
17
17
  export default {
18
- title: 'react-shell/StorybookDialog',
18
+ title: 'sdk/react-shell/StorybookDialog',
19
19
  component: StorybookDialog,
20
- actions: { argTypesRegex: '^on.*' },
21
20
  decorators: [withTheme],
22
21
  };
23
22
 
@@ -55,7 +55,7 @@ const StorybookViewport = (props: StorybookViewportProps) => {
55
55
  };
56
56
 
57
57
  export default {
58
- title: 'react-shell/StorybookViewport',
58
+ title: 'sdk/react-shell/StorybookViewport',
59
59
  component: StorybookViewport,
60
60
  decorators: [withTheme],
61
61
  parameters: { chromatic: { disableSnapshot: false } },
@@ -88,9 +88,9 @@ export const Shell = ({ runtime }: { runtime: ShellRuntime }) => {
88
88
  }
89
89
  initialInvitationCode={invitationCode}
90
90
  onCancelResetStorage={() => runtime.setLayout({ layout: ShellLayout.IDENTITY })}
91
- onDone={() => {
91
+ onDone={async () => {
92
92
  blurActiveElement();
93
- void runtime.setAppContext({ display: ShellDisplay.NONE });
93
+ await runtime.setAppContext({ display: ShellDisplay.NONE });
94
94
  runtime.setLayout({ layout: ShellLayout.DEFAULT });
95
95
  }}
96
96
  />
@@ -110,7 +110,8 @@ export const Shell = ({ runtime }: { runtime: ShellRuntime }) => {
110
110
  onJoinNewIdentity={async () => {
111
111
  runtime.setLayout({ layout: ShellLayout.STATUS });
112
112
  await client.reset();
113
- return runtime.setLayout({ layout: ShellLayout.INITIALIZE_IDENTITY_FROM_INVITATION });
113
+ // TODO(wittjosiah): Enter join flow without reloading.
114
+ return runtime.setAppContext({ display: ShellDisplay.NONE, reset: true, target: 'deviceInvitation' });
114
115
  }}
115
116
  onDone={async () => {
116
117
  blurActiveElement();
@@ -27,7 +27,7 @@ export const runShell = async (config: Config = new Config()) => {
27
27
  createRoot(document.getElementById('root')!).render(
28
28
  <StrictMode>
29
29
  <ThemeProvider tx={defaultTx} resourceExtensions={[osTranslations]}>
30
- <ClientProvider config={config} services={services}>
30
+ <ClientProvider config={config} services={services} noBanner>
31
31
  <ClipboardProvider>
32
32
  <Tooltip.Provider>
33
33
  <AgentHostingProvider>
@@ -37,9 +37,14 @@ const noOpProps: IdentityPanelImplProps = {
37
37
  };
38
38
 
39
39
  export default {
40
- title: 'react-shell/IdentityPanel',
40
+ title: 'sdk/react-shell/IdentityPanel',
41
41
  decorators: [withTheme],
42
- parameters: { translations: [osTranslations], chromatic: { disableSnapshot: false } },
42
+ parameters: {
43
+ translations: [osTranslations],
44
+ chromatic: {
45
+ disableSnapshot: false,
46
+ },
47
+ },
43
48
  };
44
49
 
45
50
  export const IdentityActionChooser = () => {
@@ -12,7 +12,7 @@ import { type Identity, useIdentity, useDevices, useHaloInvitations } from '@dxo
12
12
  import { useInvitationStatus } from '@dxos/react-client/invitations';
13
13
  import { type CancellableInvitationObservable } from '@dxos/react-client/invitations';
14
14
  import { useNetworkStatus, ConnectionState } from '@dxos/react-client/mesh';
15
- import { Avatar, DensityProvider, Input, Toolbar, Tooltip, useId, useTranslation } from '@dxos/react-ui';
15
+ import { Avatar, Input, Toolbar, Tooltip, useId, useTranslation } from '@dxos/react-ui';
16
16
  import { getSize } from '@dxos/react-ui-theme';
17
17
  import { hexToEmoji, hexToHue, keyToFallback } from '@dxos/util';
18
18
 
@@ -178,7 +178,7 @@ export const IdentityPanelImpl = (props: IdentityPanelImplProps) => {
178
178
  const onCancelReset = () => rest.send?.('unchooseAction');
179
179
 
180
180
  return (
181
- <DensityProvider density='fine'>
181
+ <>
182
182
  <IdentityHeading
183
183
  {...{ identity, titleId, title, onDone, onUpdateProfile, connectionState, onChangeConnectionState }}
184
184
  />
@@ -218,7 +218,7 @@ export const IdentityPanelImpl = (props: IdentityPanelImplProps) => {
218
218
  </Viewport.View>
219
219
  </Viewport.Views>
220
220
  </Viewport.Root>
221
- </DensityProvider>
221
+ </>
222
222
  );
223
223
  };
224
224
 
@@ -2,7 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import type { cloneElement } from 'react';
5
+ import type { FC, cloneElement } from 'react';
6
6
  import type { Event, SingleOrArray } from 'xstate';
7
7
 
8
8
  import type { Device, Identity } from '@dxos/react-client/halo';
@@ -10,7 +10,7 @@ import type { InvitationStatus } from '@dxos/react-client/invitations';
10
10
  import type { ConnectionState } from '@dxos/react-client/mesh';
11
11
 
12
12
  import type { IdentityEvent } from './identityMachine';
13
- import { type AgentFormProps } from '../../components';
13
+ import type { AgentFormProps } from '../../components';
14
14
  import type { InvitationManagerProps } from '../../steps';
15
15
 
16
16
  export type IdentityPanelInitialDisposition = 'default' | 'manage-device-invitation';
@@ -35,8 +35,8 @@ export type IdentityPanelImplProps = {
35
35
  onDone?: () => void;
36
36
  doneActionParent?: Parameters<typeof cloneElement>[0];
37
37
  invitationUrl?: string;
38
- IdentityActionChooser?: React.FC<IdentityPanelStepProps>;
39
- InvitationManager?: React.FC<InvitationManagerProps>;
38
+ IdentityActionChooser?: FC<IdentityPanelStepProps>;
39
+ InvitationManager?: FC<InvitationManagerProps>;
40
40
  } & Partial<InvitationStatus> &
41
41
  Partial<AgentFormProps>;
42
42
 
@@ -2,127 +2,22 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { useCallback, useEffect, useState } from 'react';
6
-
7
5
  import { type CancellableInvitation } from '@dxos/client-protocol';
8
- import { invariant } from '@dxos/invariant';
9
- import { log } from '@dxos/log';
10
- import { useClient, useAgentHostingClient } from '@dxos/react-client';
6
+ import { useClient } from '@dxos/react-client';
11
7
  import { type Identity } from '@dxos/react-client/halo';
12
- import { Invitation, InvitationEncoder } from '@dxos/react-client/invitations';
13
8
 
9
+ import { useEdgeAgentHandlers } from './useEdgeAgentsHandlers';
10
+ import { useKubeAgentHandlers } from './useKubeAgentHandlers';
14
11
  import { type AgentFormProps } from '../../components';
15
12
 
16
- export const useAgentHandlers = ({
17
- invitations,
18
- identity,
19
- }: {
13
+ export const useAgentHandlers = (params: {
20
14
  invitations: CancellableInvitation[];
21
15
  identity: Identity | null;
22
16
  }): AgentFormProps => {
23
- const [validationMessage, setValidationMessage] = useState('');
24
- const [agentStatus, setAgentStatus] = useState<AgentFormProps['agentStatus']>('getting');
25
- const agentHostingProviderClient = useAgentHostingClient();
26
17
  const client = useClient();
27
-
28
- const updateAgentStatus = async () => {
29
- // TODO(nf): if agent exists, subscribe to updates instead of oneshot status
30
- if (agentHostingProviderClient) {
31
- setAgentStatus('getting');
32
- try {
33
- const agentId = await agentHostingProviderClient?.getAgent(identity?.identityKey.truncate() ?? 'never');
34
- if (agentId) {
35
- setAgentStatus('created');
36
- } else {
37
- setAgentStatus('creatable');
38
- }
39
- } catch (err: any) {
40
- setAgentStatus('error');
41
- setValidationMessage(`Couldn’t check agent status: ${err.message}`);
42
- }
43
- } else {
44
- setAgentStatus('error');
45
- setValidationMessage('No agent provider configured.');
46
- }
47
- };
48
-
49
- const createAgent = async (invitationCode: string) => {
50
- invariant(identity, 'Identity not found');
51
- if (agentHostingProviderClient) {
52
- setAgentStatus('creating');
53
- setValidationMessage('');
54
- try {
55
- const agentId = await agentHostingProviderClient?.createAgent(invitationCode, identity.identityKey.truncate());
56
- if (agentId) {
57
- setAgentStatus('created');
58
- } else {
59
- setAgentStatus('creatable');
60
- setValidationMessage('Error creating agent.');
61
- }
62
- } catch (err: any) {
63
- setValidationMessage(`Error creating agent: ${err.message}`);
64
- return updateAgentStatus();
65
- }
66
- } else {
67
- setAgentStatus('error');
68
- setValidationMessage('No agent provider configured.');
69
- }
70
- };
71
-
72
- const destroyAgent = async () => {
73
- invariant(identity, 'Identity not found');
74
- if (agentHostingProviderClient) {
75
- setAgentStatus('destroying');
76
- setValidationMessage('');
77
- try {
78
- await agentHostingProviderClient?.destroyAgent(identity.identityKey.truncate());
79
- setAgentStatus('creatable');
80
- } catch (err: any) {
81
- setValidationMessage(`Error destroying agent: ${err.message}`);
82
- return updateAgentStatus();
83
- }
84
- } else {
85
- setAgentStatus('error');
86
- setValidationMessage('No agent provider configured.');
87
- }
88
- };
89
-
90
- useEffect(() => {
91
- void updateAgentStatus();
92
- }, []);
93
-
94
- const handleAgentCreate = useCallback(async (invitation: Invitation) => {
95
- const invitationCode = InvitationEncoder.encode(invitation);
96
- if (invitation.state === Invitation.State.CONNECTING) {
97
- log.info(JSON.stringify({ invitationCode, authCode: invitation.authCode }));
98
- return createAgent(invitationCode);
99
- }
100
- }, []);
101
-
102
- const onAgentCreate = async () => {
103
- invitations.forEach((invitation) => invitation.cancel());
104
-
105
- // TODO(nf): do this work in the hosting provider client?
106
- const invitation = client.halo.share({
107
- type: Invitation.Type.INTERACTIVE,
108
- authMethod: Invitation.AuthMethod.NONE,
109
- multiUse: true,
110
- });
111
-
112
- invitation.subscribe(handleAgentCreate);
113
- };
114
-
115
- const onAgentRefresh = async () => {
116
- setValidationMessage('');
117
- await updateAgentStatus();
118
- };
119
-
120
- return {
121
- onAgentCreate,
122
- onAgentDestroy: destroyAgent,
123
- onAgentRefresh,
124
- validationMessage,
125
- agentStatus,
126
- agentHostingEnabled: !!agentHostingProviderClient,
127
- };
18
+ if (client.config.values?.runtime?.client?.edgeFeatures?.agents) {
19
+ return useEdgeAgentHandlers(params);
20
+ } else {
21
+ return useKubeAgentHandlers(params);
22
+ }
128
23
  };
@@ -0,0 +1,96 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { useEffect, useState } from 'react';
6
+
7
+ import { type CancellableInvitation } from '@dxos/client-protocol';
8
+ import { invariant } from '@dxos/invariant';
9
+ import { QueryAgentStatusResponse } from '@dxos/protocols/proto/dxos/client/services';
10
+ import { useClient } from '@dxos/react-client';
11
+ import { type Identity } from '@dxos/react-client/halo';
12
+
13
+ import { type AgentFormProps } from '../../components';
14
+
15
+ export const useEdgeAgentHandlers = ({
16
+ identity,
17
+ }: {
18
+ invitations: CancellableInvitation[];
19
+ identity: Identity | null;
20
+ }): AgentFormProps => {
21
+ const [validationMessage, setValidationMessage] = useState('');
22
+ const [lastReceivedStatus, setLastReceivedStatus] = useState<QueryAgentStatusResponse.AgentStatus>(
23
+ QueryAgentStatusResponse.AgentStatus.UNKNOWN,
24
+ );
25
+ const [agentStatus, setAgentStatus] = useState<AgentFormProps['agentStatus']>('getting');
26
+ const client = useClient();
27
+ const agentHostingEnabled = Boolean(client.config.values.runtime?.client?.edgeFeatures?.agents);
28
+
29
+ const handleStatus = (status: QueryAgentStatusResponse.AgentStatus) => {
30
+ switch (status) {
31
+ case QueryAgentStatusResponse.AgentStatus.ACTIVE:
32
+ case QueryAgentStatusResponse.AgentStatus.INACTIVE:
33
+ setAgentStatus('created');
34
+ break;
35
+ case QueryAgentStatusResponse.AgentStatus.NOT_FOUND:
36
+ setAgentStatus('creatable');
37
+ break;
38
+ case QueryAgentStatusResponse.AgentStatus.UNKNOWN:
39
+ setAgentStatus('getting');
40
+ break;
41
+ }
42
+ };
43
+
44
+ useEffect(() => {
45
+ const stream = client.services.services.EdgeAgentService?.queryAgentStatus();
46
+ stream?.subscribe(({ status }) => {
47
+ setLastReceivedStatus(status);
48
+ handleStatus(status);
49
+ });
50
+ return () => {
51
+ void stream?.close().catch(() => {});
52
+ };
53
+ }, []);
54
+
55
+ const onAgentCreate = async () => {
56
+ invariant(identity, 'Identity not found');
57
+ const service = client.services.services.EdgeAgentService;
58
+ if (!service) {
59
+ setAgentStatus('error');
60
+ setValidationMessage('Agent service not initialized.');
61
+ return;
62
+ }
63
+ if (!agentHostingEnabled) {
64
+ setAgentStatus('error');
65
+ setValidationMessage('Edge agents are disabled in config.');
66
+ return;
67
+ }
68
+ setAgentStatus('creating');
69
+ setValidationMessage('');
70
+ try {
71
+ await service.createAgent();
72
+ setAgentStatus('created');
73
+ } catch (err: any) {
74
+ setAgentStatus('error');
75
+ setValidationMessage(`Error creating an agent: ${err.message}`);
76
+ }
77
+ };
78
+
79
+ const onAgentDestroy = async () => {
80
+ setValidationMessage('Agents are non-destructible.');
81
+ };
82
+
83
+ const onAgentRefresh = async () => {
84
+ setValidationMessage('');
85
+ handleStatus(lastReceivedStatus);
86
+ };
87
+
88
+ return {
89
+ onAgentCreate,
90
+ onAgentDestroy,
91
+ onAgentRefresh,
92
+ validationMessage,
93
+ agentStatus,
94
+ agentHostingEnabled,
95
+ };
96
+ };
@@ -0,0 +1,128 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { useCallback, useEffect, useState } from 'react';
6
+
7
+ import { type CancellableInvitation } from '@dxos/client-protocol';
8
+ import { invariant } from '@dxos/invariant';
9
+ import { log } from '@dxos/log';
10
+ import { useClient, useAgentHostingClient } from '@dxos/react-client';
11
+ import { type Identity } from '@dxos/react-client/halo';
12
+ import { Invitation, InvitationEncoder } from '@dxos/react-client/invitations';
13
+
14
+ import { type AgentFormProps } from '../../components';
15
+
16
+ export const useKubeAgentHandlers = ({
17
+ invitations,
18
+ identity,
19
+ }: {
20
+ invitations: CancellableInvitation[];
21
+ identity: Identity | null;
22
+ }): AgentFormProps => {
23
+ const [validationMessage, setValidationMessage] = useState('');
24
+ const [agentStatus, setAgentStatus] = useState<AgentFormProps['agentStatus']>('getting');
25
+ const agentHostingProviderClient = useAgentHostingClient();
26
+ const client = useClient();
27
+
28
+ const updateAgentStatus = async () => {
29
+ // TODO(nf): if agent exists, subscribe to updates instead of oneshot status
30
+ if (agentHostingProviderClient) {
31
+ setAgentStatus('getting');
32
+ try {
33
+ const agentId = await agentHostingProviderClient?.getAgent(identity?.identityKey.truncate() ?? 'never');
34
+ if (agentId) {
35
+ setAgentStatus('created');
36
+ } else {
37
+ setAgentStatus('creatable');
38
+ }
39
+ } catch (err: any) {
40
+ setAgentStatus('error');
41
+ setValidationMessage(`Couldn’t check agent status: ${err.message}`);
42
+ }
43
+ } else {
44
+ setAgentStatus('error');
45
+ setValidationMessage('No agent provider configured.');
46
+ }
47
+ };
48
+
49
+ const createAgent = async (invitationCode: string) => {
50
+ invariant(identity, 'Identity not found');
51
+ if (agentHostingProviderClient) {
52
+ setAgentStatus('creating');
53
+ setValidationMessage('');
54
+ try {
55
+ const agentId = await agentHostingProviderClient?.createAgent(invitationCode, identity.identityKey.truncate());
56
+ if (agentId) {
57
+ setAgentStatus('created');
58
+ } else {
59
+ setAgentStatus('creatable');
60
+ setValidationMessage('Error creating agent.');
61
+ }
62
+ } catch (err: any) {
63
+ setValidationMessage(`Error creating agent: ${err.message}`);
64
+ return updateAgentStatus();
65
+ }
66
+ } else {
67
+ setAgentStatus('error');
68
+ setValidationMessage('No agent provider configured.');
69
+ }
70
+ };
71
+
72
+ const destroyAgent = async () => {
73
+ invariant(identity, 'Identity not found');
74
+ if (agentHostingProviderClient) {
75
+ setAgentStatus('destroying');
76
+ setValidationMessage('');
77
+ try {
78
+ await agentHostingProviderClient?.destroyAgent(identity.identityKey.truncate());
79
+ setAgentStatus('creatable');
80
+ } catch (err: any) {
81
+ setValidationMessage(`Error destroying agent: ${err.message}`);
82
+ return updateAgentStatus();
83
+ }
84
+ } else {
85
+ setAgentStatus('error');
86
+ setValidationMessage('No agent provider configured.');
87
+ }
88
+ };
89
+
90
+ useEffect(() => {
91
+ void updateAgentStatus();
92
+ }, []);
93
+
94
+ const handleAgentCreate = useCallback(async (invitation: Invitation) => {
95
+ const invitationCode = InvitationEncoder.encode(invitation);
96
+ if (invitation.state === Invitation.State.CONNECTING) {
97
+ log.info(JSON.stringify({ invitationCode, authCode: invitation.authCode }));
98
+ return createAgent(invitationCode);
99
+ }
100
+ }, []);
101
+
102
+ const onAgentCreate = async () => {
103
+ invitations.forEach((invitation) => invitation.cancel());
104
+
105
+ // TODO(nf): do this work in the hosting provider client?
106
+ const invitation = client.halo.share({
107
+ type: Invitation.Type.INTERACTIVE,
108
+ authMethod: Invitation.AuthMethod.NONE,
109
+ multiUse: true,
110
+ });
111
+
112
+ invitation.subscribe(handleAgentCreate);
113
+ };
114
+
115
+ const onAgentRefresh = async () => {
116
+ setValidationMessage('');
117
+ await updateAgentStatus();
118
+ };
119
+
120
+ return {
121
+ onAgentCreate,
122
+ onAgentDestroy: destroyAgent,
123
+ onAgentRefresh,
124
+ validationMessage,
125
+ agentStatus,
126
+ agentHostingEnabled: !!agentHostingProviderClient,
127
+ };
128
+ };
@@ -13,6 +13,7 @@ import { type JoinPanelImplProps } from './JoinPanelProps';
13
13
  import { IdentityInputImpl } from './steps';
14
14
  import { StorybookDialog } from '../../components/StorybookDialog';
15
15
  import { ConfirmResetImpl } from '../../steps';
16
+ import { osTranslations as translations } from '../../translations';
16
17
 
17
18
  const noOpProps: JoinPanelImplProps = {
18
19
  titleId: 'storybookJoinPanel__title',
@@ -29,10 +30,10 @@ const JoinDialog = (args: Partial<JoinPanelImplProps>) => (
29
30
  );
30
31
 
31
32
  export default {
32
- title: 'react-shell/JoinPanel',
33
+ title: 'sdk/react-shell/JoinPanel',
33
34
  component: JoinDialog,
34
35
  decorators: [withTheme],
35
- parameters: { chromatic: { disableSnapshot: false } },
36
+ parameters: { translations, chromatic: { disableSnapshot: false } },
36
37
  };
37
38
 
38
39
  export const AdditionMethodChooser = () => <JoinDialog mode='halo-only' activeView='addition method chooser' />;
@@ -7,7 +7,7 @@ import React, { useCallback, useEffect, useMemo } from 'react';
7
7
  import { log } from '@dxos/log';
8
8
  import { useClient, useMulticastObservable } from '@dxos/react-client';
9
9
  import { useIdentity } from '@dxos/react-client/halo';
10
- import { DensityProvider, useId, useThemeContext } from '@dxos/react-ui';
10
+ import { useId, useThemeContext } from '@dxos/react-ui';
11
11
 
12
12
  import { JoinHeading } from './JoinHeading';
13
13
  import { type JoinPanelImplProps, type JoinPanelProps } from './JoinPanelProps';
@@ -16,7 +16,6 @@ import {
16
16
  AdditionMethodChooser,
17
17
  IdentityAdded,
18
18
  IdentityInput,
19
- InvitationAccepted,
20
19
  InvitationAuthenticator,
21
20
  InvitationInput,
22
21
  InvitationRescuer,
@@ -35,11 +34,12 @@ export const JoinPanelImpl = (props: JoinPanelImplProps) => {
35
34
  unredeemedCodes,
36
35
  invitationIds,
37
36
  invitationStates,
37
+ invitationAuthMethods,
38
38
  succeededKeys,
39
39
  failReasons,
40
40
  onExit,
41
41
  onHaloDone,
42
- onSpaceDone,
42
+ // onSpaceDone,
43
43
  exitActionParent,
44
44
  doneActionParent,
45
45
  onHaloInvitationCancel,
@@ -52,7 +52,7 @@ export const JoinPanelImpl = (props: JoinPanelImplProps) => {
52
52
  ConfirmReset: ConfirmResetComponent = ConfirmReset,
53
53
  } = props;
54
54
  return (
55
- <DensityProvider density='fine'>
55
+ <>
56
56
  {mode !== 'halo-only' && <JoinHeading {...{ titleId, mode, onExit, exitActionParent }} />}
57
57
  <Viewport.Root focusManaged activeView={activeView}>
58
58
  <Viewport.Views>
@@ -105,13 +105,14 @@ export const JoinPanelImpl = (props: JoinPanelImplProps) => {
105
105
  send={send}
106
106
  Kind='Halo'
107
107
  invitationId={invitationIds?.Halo}
108
+ authMethod={invitationAuthMethods?.Halo}
108
109
  active={activeView === 'halo invitation authenticator'}
109
110
  onInvitationCancel={onHaloInvitationCancel}
110
111
  onInvitationAuthenticate={onHaloInvitationAuthenticate}
111
112
  {...(failed.has('Halo') && { failed: true })}
112
113
  />
113
114
  </Viewport.View>
114
- <Viewport.View classNames={stepStyles} id='halo invitation accepted'>
115
+ {/* <Viewport.View classNames={stepStyles} id='halo invitation accepted'>
115
116
  <InvitationAccepted
116
117
  {...{
117
118
  send,
@@ -121,7 +122,7 @@ export const JoinPanelImpl = (props: JoinPanelImplProps) => {
121
122
  onDone: onHaloDone,
122
123
  }}
123
124
  />
124
- </Viewport.View>
125
+ </Viewport.View> */}
125
126
  <Viewport.View classNames={stepStyles} id='identity added'>
126
127
  <IdentityAdded
127
128
  {...{ send, mode, active: activeView === 'identity added', doneActionParent, onDone: onHaloDone }}
@@ -153,13 +154,14 @@ export const JoinPanelImpl = (props: JoinPanelImplProps) => {
153
154
  send={send}
154
155
  Kind='Space'
155
156
  invitationId={invitationIds?.Space}
157
+ authMethod={invitationAuthMethods?.Space}
156
158
  active={activeView === 'space invitation authenticator'}
157
159
  onInvitationCancel={onSpaceInvitationCancel}
158
160
  onInvitationAuthenticate={onSpaceInvitationAuthenticate}
159
161
  {...(failed.has('Space') && { failed: true })}
160
162
  />
161
163
  </Viewport.View>
162
- <Viewport.View classNames={stepStyles} id='space invitation accepted'>
164
+ {/* <Viewport.View classNames={stepStyles} id='space invitation accepted'>
163
165
  <InvitationAccepted
164
166
  {...{
165
167
  send,
@@ -169,10 +171,10 @@ export const JoinPanelImpl = (props: JoinPanelImplProps) => {
169
171
  onDone: onSpaceDone,
170
172
  }}
171
173
  />
172
- </Viewport.View>
174
+ </Viewport.View> */}
173
175
  </Viewport.Views>
174
176
  </Viewport.Root>
175
- </DensityProvider>
177
+ </>
176
178
  );
177
179
  };
178
180
 
@@ -315,6 +317,17 @@ export const JoinPanel = ({
315
317
  }
316
318
  }, [joinState]);
317
319
 
320
+ useEffect(() => {
321
+ switch (activeView) {
322
+ case 'halo invitation accepted':
323
+ onHaloDone();
324
+ break;
325
+ case 'space invitation accepted':
326
+ onSpaceDone();
327
+ break;
328
+ }
329
+ }, [activeView]);
330
+
318
331
  const failed = useMemo(() => {
319
332
  const result: JoinPanelImplProps['failed'] = new Set();
320
333
  switch (true) {
@@ -363,6 +376,14 @@ export const JoinPanel = ({
363
376
  [joinState],
364
377
  );
365
378
 
379
+ const invitationAuthMethods = useMemo(
380
+ () => ({
381
+ Halo: joinState.context.halo.invitationObservable?.get().authMethod,
382
+ Space: joinState.context.space.invitationObservable?.get().authMethod,
383
+ }),
384
+ [joinState],
385
+ );
386
+
366
387
  const invitationIds = useMemo(
367
388
  () => ({
368
389
  Halo: joinState.context.halo.invitationObservable?.get().invitationId,
@@ -418,6 +439,7 @@ export const JoinPanel = ({
418
439
  unredeemedCodes,
419
440
  invitationIds,
420
441
  invitationStates,
442
+ invitationAuthMethods,
421
443
  succeededKeys,
422
444
  identity,
423
445
  onExit,