@dxos/client-services 0.6.13 → 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 (138) hide show
  1. package/dist/lib/browser/{chunk-CRXXOI45.mjs → chunk-BBLK6AUB.mjs} +6301 -5383
  2. package/dist/lib/browser/chunk-BBLK6AUB.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +320 -45
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +12 -7
  7. package/dist/lib/browser/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/{chunk-PZ3JJJ3K.cjs → chunk-SJEDBF7U.cjs} +6286 -5378
  9. package/dist/lib/node/chunk-SJEDBF7U.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +369 -94
  11. package/dist/lib/node/index.cjs.map +4 -4
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/testing/index.cjs +18 -13
  14. package/dist/lib/node/testing/index.cjs.map +3 -3
  15. package/dist/lib/node-esm/chunk-A6GS6OOD.mjs +8634 -0
  16. package/dist/lib/node-esm/chunk-A6GS6OOD.mjs.map +7 -0
  17. package/dist/lib/node-esm/index.mjs +691 -0
  18. package/dist/lib/node-esm/index.mjs.map +7 -0
  19. package/dist/lib/node-esm/meta.json +1 -0
  20. package/dist/lib/node-esm/testing/index.mjs +424 -0
  21. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  22. package/dist/types/src/index.d.ts +1 -0
  23. package/dist/types/src/index.d.ts.map +1 -1
  24. package/dist/types/src/packlets/agents/edge-agent-manager.d.ts +35 -0
  25. package/dist/types/src/packlets/agents/edge-agent-manager.d.ts.map +1 -0
  26. package/dist/types/src/packlets/agents/edge-agent-service.d.ts +10 -0
  27. package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -0
  28. package/dist/types/src/packlets/agents/index.d.ts +3 -0
  29. package/dist/types/src/packlets/agents/index.d.ts.map +1 -0
  30. package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -1
  31. package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
  32. package/dist/types/src/packlets/identity/authenticator.node.test.d.ts +2 -0
  33. package/dist/types/src/packlets/identity/authenticator.node.test.d.ts.map +1 -0
  34. package/dist/types/src/packlets/identity/contacts-service.d.ts +1 -1
  35. package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
  36. package/dist/types/src/packlets/identity/identity-manager.d.ts +28 -9
  37. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  38. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +18 -0
  39. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -0
  40. package/dist/types/src/packlets/identity/identity-service.d.ts +7 -2
  41. package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
  42. package/dist/types/src/packlets/identity/identity.d.ts +12 -3
  43. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  44. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
  45. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts +30 -0
  46. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -0
  47. package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts +2 -1
  48. package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -1
  49. package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts +2 -1
  50. package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
  51. package/dist/types/src/packlets/invitations/invitation-state.d.ts +19 -0
  52. package/dist/types/src/packlets/invitations/invitation-state.d.ts.map +1 -0
  53. package/dist/types/src/packlets/invitations/invitations-handler.d.ts +8 -8
  54. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  55. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  56. package/dist/types/src/packlets/services/service-context.d.ts +14 -9
  57. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  58. package/dist/types/src/packlets/services/service-host.d.ts +2 -0
  59. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  60. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +7 -3
  61. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  62. package/dist/types/src/packlets/spaces/data-space.d.ts +5 -3
  63. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  64. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts +3 -0
  65. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
  66. package/dist/types/src/packlets/spaces/edge-feed-replicator.test.d.ts +2 -0
  67. package/dist/types/src/packlets/spaces/edge-feed-replicator.test.d.ts.map +1 -0
  68. package/dist/types/src/packlets/spaces/epoch-migrations.d.ts +1 -1
  69. package/dist/types/src/packlets/spaces/epoch-migrations.d.ts.map +1 -1
  70. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +35 -6
  71. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
  72. package/dist/types/src/packlets/spaces/spaces-service.d.ts +1 -1
  73. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  74. package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
  75. package/dist/types/src/packlets/testing/test-builder.d.ts +1 -2
  76. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  77. package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
  78. package/dist/types/src/testing/setup.d.ts +3 -0
  79. package/dist/types/src/testing/setup.d.ts.map +1 -0
  80. package/dist/types/src/version.d.ts +1 -1
  81. package/dist/types/src/version.d.ts.map +1 -1
  82. package/package.json +44 -39
  83. package/src/index.ts +1 -0
  84. package/src/packlets/agents/edge-agent-manager.ts +163 -0
  85. package/src/packlets/agents/edge-agent-service.ts +42 -0
  86. package/src/packlets/agents/index.ts +6 -0
  87. package/src/packlets/devices/devices-service.test.ts +4 -5
  88. package/src/packlets/diagnostics/diagnostics-broadcast.ts +1 -0
  89. package/src/packlets/identity/{authenticator.test.ts → authenticator.node.test.ts} +2 -3
  90. package/src/packlets/identity/authenticator.ts +5 -2
  91. package/src/packlets/identity/contacts-service.ts +1 -1
  92. package/src/packlets/identity/identity-manager.test.ts +31 -16
  93. package/src/packlets/identity/identity-manager.ts +76 -32
  94. package/src/packlets/identity/identity-recovery-manager.ts +95 -0
  95. package/src/packlets/identity/identity-service.test.ts +5 -8
  96. package/src/packlets/identity/identity-service.ts +11 -5
  97. package/src/packlets/identity/identity.test.ts +130 -239
  98. package/src/packlets/identity/identity.ts +60 -17
  99. package/src/packlets/invitations/device-invitation-protocol.test.ts +7 -4
  100. package/src/packlets/invitations/device-invitation-protocol.ts +5 -1
  101. package/src/packlets/invitations/edge-invitation-handler.ts +185 -0
  102. package/src/packlets/invitations/invitation-guest-extenstion.ts +8 -4
  103. package/src/packlets/invitations/invitation-host-extension.ts +8 -7
  104. package/src/packlets/invitations/invitation-state.ts +111 -0
  105. package/src/packlets/invitations/invitations-handler.test.ts +16 -9
  106. package/src/packlets/invitations/invitations-handler.ts +23 -93
  107. package/src/packlets/invitations/space-invitation-protocol.test.ts +4 -3
  108. package/src/packlets/invitations/space-invitation-protocol.ts +4 -0
  109. package/src/packlets/logging/logging.test.ts +1 -2
  110. package/src/packlets/network/network-service.test.ts +2 -3
  111. package/src/packlets/services/service-context.test.ts +3 -1
  112. package/src/packlets/services/service-context.ts +113 -35
  113. package/src/packlets/services/service-host.test.ts +8 -12
  114. package/src/packlets/services/service-host.ts +25 -7
  115. package/src/packlets/services/service-registry.test.ts +1 -2
  116. package/src/packlets/spaces/data-space-manager.test.ts +2 -2
  117. package/src/packlets/spaces/data-space-manager.ts +44 -7
  118. package/src/packlets/spaces/data-space.ts +37 -6
  119. package/src/packlets/spaces/edge-feed-replicator.test.ts +252 -0
  120. package/src/packlets/spaces/edge-feed-replicator.ts +80 -22
  121. package/src/packlets/spaces/epoch-migrations.ts +2 -2
  122. package/src/packlets/spaces/notarization-plugin.test.ts +10 -7
  123. package/src/packlets/spaces/notarization-plugin.ts +196 -29
  124. package/src/packlets/spaces/spaces-service.test.ts +5 -9
  125. package/src/packlets/spaces/spaces-service.ts +6 -1
  126. package/src/packlets/storage/storage.ts +0 -1
  127. package/src/packlets/system/system-service.test.ts +1 -2
  128. package/src/packlets/testing/test-builder.ts +7 -4
  129. package/src/packlets/worker/worker-runtime.ts +2 -2
  130. package/src/testing/setup.ts +11 -0
  131. package/src/version.ts +1 -5
  132. package/dist/lib/browser/chunk-CRXXOI45.mjs.map +0 -7
  133. package/dist/lib/node/chunk-PZ3JJJ3K.cjs.map +0 -7
  134. package/dist/types/src/packlets/identity/authenticator.test.d.ts +0 -2
  135. package/dist/types/src/packlets/identity/authenticator.test.d.ts.map +0 -1
  136. package/dist/types/src/packlets/services/automerge-host.test.d.ts +0 -2
  137. package/dist/types/src/packlets/services/automerge-host.test.d.ts.map +0 -1
  138. package/src/packlets/services/automerge-host.test.ts +0 -60
@@ -2,12 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { expect } from 'chai';
5
+ import { afterEach, beforeEach, describe, expect, test } from 'vitest';
6
6
 
7
7
  import { Trigger } from '@dxos/async';
8
8
  import { log, LogLevel } from '@dxos/log';
9
9
  import { type LogEntry } from '@dxos/protocols/proto/dxos/client/services';
10
- import { beforeEach, describe, test } from '@dxos/test';
11
10
 
12
11
  import { LoggingServiceImpl } from './logging-service';
13
12
 
@@ -2,12 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { expect } from 'chai';
5
+ import { afterEach, onTestFinished, beforeEach, describe, expect, test } from 'vitest';
6
6
 
7
7
  import { Trigger } from '@dxos/async';
8
8
  import { Context } from '@dxos/context';
9
9
  import { type NetworkService, ConnectionState } from '@dxos/protocols/proto/dxos/client/services';
10
- import { afterEach, afterTest, beforeEach, describe, test } from '@dxos/test';
11
10
 
12
11
  import { NetworkServiceImpl } from './network-service';
13
12
  import { type ServiceContext } from '../services';
@@ -41,7 +40,7 @@ describe('NetworkService', () => {
41
40
  query.subscribe(({ swarm }) => {
42
41
  result.wake(swarm);
43
42
  });
44
- afterTest(() => query.close());
43
+ onTestFinished(() => query.close());
45
44
  expect(await result.wait()).to.equal(ConnectionState.ONLINE);
46
45
 
47
46
  result = new Trigger<ConnectionState | undefined>();
@@ -2,9 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { describe, test } from 'vitest';
6
+
5
7
  import { MemorySignalManagerContext, MemorySignalManager } from '@dxos/messaging';
6
8
  import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
7
- import { describe, openAndClose, test } from '@dxos/test';
9
+ import { openAndClose } from '@dxos/test-utils';
8
10
 
9
11
  import { createServiceContext, performInvitation } from '../testing';
10
12
 
@@ -2,13 +2,20 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import { Trigger } from '@dxos/async';
5
+ import { Mutex, Trigger } from '@dxos/async';
6
6
  import { Context, Resource } from '@dxos/context';
7
7
  import { getCredentialAssertion, type CredentialProcessor } from '@dxos/credentials';
8
- import { failUndefined } from '@dxos/debug';
9
- import { EchoEdgeReplicator, EchoHost } from '@dxos/echo-db';
10
- import { MeshEchoReplicator, MetadataStore, SpaceManager, valueEncoding } from '@dxos/echo-pipeline';
11
- import type { EdgeConnection } from '@dxos/edge-client';
8
+ import { failUndefined, warnAfterTimeout } from '@dxos/debug';
9
+ import {
10
+ EchoEdgeReplicator,
11
+ EchoHost,
12
+ MeshEchoReplicator,
13
+ MetadataStore,
14
+ SpaceManager,
15
+ valueEncoding,
16
+ } from '@dxos/echo-pipeline';
17
+ import { createChainEdgeIdentity, createEphemeralEdgeIdentity } from '@dxos/edge-client';
18
+ import type { EdgeHttpClient, EdgeConnection, EdgeIdentity } from '@dxos/edge-client';
12
19
  import { FeedFactory, FeedStore } from '@dxos/feed-store';
13
20
  import { invariant } from '@dxos/invariant';
14
21
  import { Keyring } from '@dxos/keyring';
@@ -23,19 +30,21 @@ import { type Runtime } from '@dxos/protocols/proto/dxos/config';
23
30
  import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
24
31
  import { type Credential, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
25
32
  import { type Storage } from '@dxos/random-access-storage';
26
- import type { TeleportParams } from '@dxos/teleport';
27
33
  import { BlobStore } from '@dxos/teleport-extension-object-sync';
28
34
  import { trace as Trace } from '@dxos/tracing';
29
35
  import { safeInstanceof } from '@dxos/util';
30
36
 
37
+ import { EdgeAgentManager } from '../agents';
31
38
  import {
32
39
  IdentityManager,
33
40
  type CreateIdentityOptions,
34
- type IdentityManagerRuntimeParams,
41
+ type IdentityManagerParams,
35
42
  type JoinIdentityParams,
36
43
  } from '../identity';
44
+ import { EdgeIdentityRecoveryManager } from '../identity/identity-recovery-manager';
37
45
  import {
38
46
  DeviceInvitationProtocol,
47
+ type InvitationConnectionParams,
39
48
  InvitationsHandler,
40
49
  InvitationsManager,
41
50
  SpaceInvitationProtocol,
@@ -43,9 +52,12 @@ import {
43
52
  } from '../invitations';
44
53
  import { DataSpaceManager, type DataSpaceManagerRuntimeParams, type SigningContext } from '../spaces';
45
54
 
46
- export type ServiceContextRuntimeParams = IdentityManagerRuntimeParams &
55
+ export type ServiceContextRuntimeParams = Pick<
56
+ IdentityManagerParams,
57
+ 'devicePresenceOfflineTimeout' | 'devicePresenceAnnounceInterval'
58
+ > &
47
59
  DataSpaceManagerRuntimeParams & {
48
- invitationConnectionDefaultParams?: Partial<TeleportParams>;
60
+ invitationConnectionDefaultParams?: InvitationConnectionParams;
49
61
  disableP2pReplication?: boolean;
50
62
  };
51
63
  /**
@@ -56,6 +68,8 @@ export type ServiceContextRuntimeParams = IdentityManagerRuntimeParams &
56
68
  @safeInstanceof('dxos.client-services.ServiceContext')
57
69
  @Trace.resource()
58
70
  export class ServiceContext extends Resource {
71
+ private readonly _edgeIdentityUpdateMutex = new Mutex();
72
+
59
73
  public readonly initialized = new Trigger();
60
74
  public readonly metadataStore: MetadataStore;
61
75
  public readonly blobStore: BlobStore;
@@ -63,6 +77,7 @@ export class ServiceContext extends Resource {
63
77
  public readonly keyring: Keyring;
64
78
  public readonly spaceManager: SpaceManager;
65
79
  public readonly identityManager: IdentityManager;
80
+ public readonly recoveryManager: EdgeIdentityRecoveryManager;
66
81
  public readonly invitations: InvitationsHandler;
67
82
  public readonly invitationsManager: InvitationsManager;
68
83
  public readonly echoHost: EchoHost;
@@ -71,6 +86,7 @@ export class ServiceContext extends Resource {
71
86
 
72
87
  // Initialized after identity is initialized.
73
88
  public dataSpaceManager?: DataSpaceManager;
89
+ public edgeAgentManager?: EdgeAgentManager;
74
90
 
75
91
  private readonly _handlerFactories = new Map<
76
92
  Invitation.Kind,
@@ -87,6 +103,7 @@ export class ServiceContext extends Resource {
87
103
  public readonly networkManager: SwarmNetworkManager,
88
104
  public readonly signalManager: SignalManager,
89
105
  private readonly _edgeConnection: EdgeConnection | undefined,
106
+ private readonly _edgeHttpClient: EdgeHttpClient | undefined,
90
107
  public readonly _runtimeParams?: ServiceContextRuntimeParams,
91
108
  private readonly _edgeFeatures?: Runtime.Client.EdgeFeatures,
92
109
  ) {
@@ -116,31 +133,22 @@ export class ServiceContext extends Resource {
116
133
  disableP2pReplication: this._runtimeParams?.disableP2pReplication,
117
134
  });
118
135
 
119
- this.identityManager = new IdentityManager(
120
- this.metadataStore,
136
+ this.identityManager = new IdentityManager({
137
+ metadataStore: this.metadataStore,
138
+ keyring: this.keyring,
139
+ feedStore: this.feedStore,
140
+ spaceManager: this.spaceManager,
141
+ devicePresenceOfflineTimeout: this._runtimeParams?.devicePresenceOfflineTimeout,
142
+ devicePresenceAnnounceInterval: this._runtimeParams?.devicePresenceAnnounceInterval,
143
+ edgeConnection: this._edgeConnection,
144
+ edgeFeatures: this._edgeFeatures,
145
+ });
146
+
147
+ this.recoveryManager = new EdgeIdentityRecoveryManager(
121
148
  this.keyring,
122
- this.feedStore,
123
- this.spaceManager,
124
- this._runtimeParams as IdentityManagerRuntimeParams,
125
- {
126
- onIdentityConstruction: (identity) => {
127
- if (this._edgeConnection) {
128
- log.info('Setting identity on edge connection', {
129
- identity: identity.identityKey.toHex(),
130
- oldIdentity: this._edgeConnection.identityKey,
131
- swarms: this.networkManager.topics,
132
- });
133
- this._edgeConnection.setIdentity({
134
- peerKey: identity.deviceKey.toHex(),
135
- identityKey: identity.identityKey.toHex(),
136
- });
137
- this.networkManager.setPeerInfo({
138
- identityKey: identity.identityKey.toHex(),
139
- peerKey: identity.deviceKey.toHex(),
140
- });
141
- }
142
- },
143
- },
149
+ this._edgeHttpClient,
150
+ () => this.identityManager.identity,
151
+ this._acceptIdentity.bind(this),
144
152
  );
145
153
 
146
154
  this.echoHost = new EchoHost({ kv: this.level });
@@ -149,6 +157,7 @@ export class ServiceContext extends Resource {
149
157
 
150
158
  this.invitations = new InvitationsHandler(
151
159
  this.networkManager, //
160
+ this._edgeHttpClient,
152
161
  _runtimeParams?.invitationConnectionDefaultParams,
153
162
  );
154
163
  this.invitationsManager = new InvitationsManager(
@@ -185,6 +194,11 @@ export class ServiceContext extends Resource {
185
194
 
186
195
  log('opening...');
187
196
  log.trace('dxos.sdk.service-context.open', trace.begin({ id: this._instanceId }));
197
+
198
+ await this.identityManager.open(ctx);
199
+
200
+ await this._setNetworkIdentity();
201
+
188
202
  await this._edgeConnection?.open();
189
203
  await this.signalManager.open();
190
204
  await this.networkManager.open();
@@ -200,9 +214,9 @@ export class ServiceContext extends Resource {
200
214
 
201
215
  await this.metadataStore.load();
202
216
  await this.spaceManager.open();
203
- await this.identityManager.open(ctx);
204
217
 
205
218
  if (this.identityManager.identity) {
219
+ await this.identityManager.identity.joinNetwork();
206
220
  await this._initialize(ctx);
207
221
  }
208
222
 
@@ -219,6 +233,7 @@ export class ServiceContext extends Resource {
219
233
  await this.identityManager.identity.space.spaceState.removeCredentialProcessor(this._deviceSpaceSync);
220
234
  }
221
235
  await this.dataSpaceManager?.close();
236
+ await this.edgeAgentManager?.close();
222
237
  await this.identityManager.close();
223
238
  await this.spaceManager.close();
224
239
  await this.feedStore.close();
@@ -234,11 +249,16 @@ export class ServiceContext extends Resource {
234
249
 
235
250
  async createIdentity(params: CreateIdentityOptions = {}) {
236
251
  const identity = await this.identityManager.createIdentity(params);
252
+ await this._setNetworkIdentity();
253
+ await identity.joinNetwork();
237
254
  await this._initialize(new Context());
238
255
  return identity;
239
256
  }
240
257
 
241
258
  getInvitationHandler(invitation: Partial<Invitation> & Pick<Invitation, 'kind'>): InvitationProtocol {
259
+ if (this.identityManager.identity == null && invitation.kind === Invitation.Kind.SPACE) {
260
+ throw new Error('Identity must be created before joining a space.');
261
+ }
242
262
  const factory = this._handlerFactories.get(invitation.kind);
243
263
  invariant(factory, `Unknown invitation kind: ${invitation.kind}`);
244
264
  return factory(invitation);
@@ -255,7 +275,10 @@ export class ServiceContext extends Resource {
255
275
  }
256
276
 
257
277
  private async _acceptIdentity(params: JoinIdentityParams) {
258
- const identity = await this.identityManager.acceptIdentity(params);
278
+ const { identity, identityRecord } = await this.identityManager.prepareIdentity(params);
279
+ await this._setNetworkIdentity({ deviceCredential: params.authorizedDeviceCredential! });
280
+ await identity.joinNetwork();
281
+ await this.identityManager.acceptIdentity(identity, identityRecord, params.deviceProfile);
259
282
  await this._initialize(new Context());
260
283
  return identity;
261
284
  }
@@ -292,6 +315,7 @@ export class ServiceContext extends Resource {
292
315
  echoHost: this.echoHost,
293
316
  invitationsManager: this.invitationsManager,
294
317
  edgeConnection: this._edgeConnection,
318
+ edgeHttpClient: this._edgeHttpClient,
295
319
  echoEdgeReplicator: this._echoEdgeReplicator,
296
320
  meshReplicator: this._meshReplicator,
297
321
  runtimeParams: this._runtimeParams as DataSpaceManagerRuntimeParams,
@@ -299,6 +323,14 @@ export class ServiceContext extends Resource {
299
323
  });
300
324
  await this.dataSpaceManager.open();
301
325
 
326
+ this.edgeAgentManager = new EdgeAgentManager(
327
+ this._edgeFeatures,
328
+ this._edgeHttpClient,
329
+ this.dataSpaceManager,
330
+ identity,
331
+ );
332
+ await this.edgeAgentManager.open();
333
+
302
334
  this._handlerFactories.set(Invitation.Kind.SPACE, (invitation) => {
303
335
  invariant(this.dataSpaceManager, 'dataSpaceManager not initialized yet');
304
336
  return new SpaceInvitationProtocol(this.dataSpaceManager, signingContext, this.keyring, invitation.spaceKey);
@@ -338,4 +370,50 @@ export class ServiceContext extends Resource {
338
370
 
339
371
  await identity.space.spaceState.addCredentialProcessor(this._deviceSpaceSync);
340
372
  }
373
+
374
+ private async _setNetworkIdentity(params?: { deviceCredential: Credential }) {
375
+ using _ = await this._edgeIdentityUpdateMutex.acquire();
376
+
377
+ let edgeIdentity: EdgeIdentity;
378
+ const identity = this.identityManager.identity;
379
+ if (identity) {
380
+ log.info('Setting identity on edge connection', {
381
+ identity: identity.identityKey.toHex(),
382
+ swarms: this.networkManager.topics,
383
+ });
384
+ if (params?.deviceCredential) {
385
+ edgeIdentity = await createChainEdgeIdentity(
386
+ identity.signer,
387
+ identity.identityKey,
388
+ identity.deviceKey,
389
+ { credential: params.deviceCredential },
390
+ [], // TODO(dmaretskyi): Service access credentials.
391
+ );
392
+ } else {
393
+ // TODO: throw here or from identity if device chain can't be loaded, to avoid indefinite hangup
394
+ await warnAfterTimeout(10_000, 'Waiting for identity to be ready for edge connection', async () => {
395
+ await identity.ready();
396
+ });
397
+
398
+ invariant(identity.deviceCredentialChain);
399
+
400
+ edgeIdentity = await createChainEdgeIdentity(
401
+ identity.signer,
402
+ identity.identityKey,
403
+ identity.deviceKey,
404
+ identity.deviceCredentialChain,
405
+ [], // TODO(dmaretskyi): Service access credentials.
406
+ );
407
+ }
408
+ } else {
409
+ edgeIdentity = await createEphemeralEdgeIdentity();
410
+ }
411
+
412
+ this._edgeConnection?.setIdentity(edgeIdentity);
413
+ this._edgeHttpClient?.setIdentity(edgeIdentity);
414
+ this.networkManager.setPeerInfo({
415
+ identityKey: edgeIdentity.identityKey,
416
+ peerKey: edgeIdentity.peerKey,
417
+ });
418
+ }
341
419
  }
@@ -2,9 +2,8 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import chai, { expect } from 'chai';
6
- import chaiAsPromised from 'chai-as-promised';
7
5
  import { rmSync } from 'node:fs';
6
+ import { afterEach, onTestFinished, describe, expect, test } from 'vitest';
8
7
 
9
8
  import { asyncTimeout, latch, Trigger } from '@dxos/async';
10
9
  import { Config } from '@dxos/config';
@@ -14,13 +13,10 @@ import { type PublicKey } from '@dxos/keys';
14
13
  import { MemorySignalManagerContext } from '@dxos/messaging';
15
14
  import { type Identity } from '@dxos/protocols/proto/dxos/client/services';
16
15
  import { type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
17
- import { afterTest, describe, test } from '@dxos/test';
18
16
  import { isNode } from '@dxos/util';
19
17
 
20
18
  import { createMockCredential, createServiceHost } from '../testing';
21
19
 
22
- chai.use(chaiAsPromised);
23
-
24
20
  describe('ClientServicesHost', () => {
25
21
  const dataRoot = '/tmp/dxos/client-services/service-host/storage';
26
22
 
@@ -38,7 +34,7 @@ describe('ClientServicesHost', () => {
38
34
  test('queryCredentials', async () => {
39
35
  const host = createServiceHost(new Config(), new MemorySignalManagerContext());
40
36
  await host.open(new Context());
41
- afterTest(() => host.close());
37
+ onTestFinished(() => host.close());
42
38
 
43
39
  await host.services.IdentityService!.createIdentity({});
44
40
  const { spaceKey } = await host.services.SpacesService!.createSpace();
@@ -49,7 +45,7 @@ describe('ClientServicesHost', () => {
49
45
  tick();
50
46
  // console.log(credential);
51
47
  });
52
- afterTest(() => stream.close());
48
+ onTestFinished(() => stream.close());
53
49
 
54
50
  await done();
55
51
  });
@@ -57,7 +53,7 @@ describe('ClientServicesHost', () => {
57
53
  test('write and query credentials', async () => {
58
54
  const host = createServiceHost(new Config(), new MemorySignalManagerContext());
59
55
  await host.open(new Context());
60
- afterTest(() => host.close());
56
+ onTestFinished(() => host.close());
61
57
 
62
58
  await host.services.IdentityService!.createIdentity({});
63
59
 
@@ -86,7 +82,7 @@ describe('ClientServicesHost', () => {
86
82
  queriedCredential.wake(credential);
87
83
  }
88
84
  });
89
- afterTest(() => credentials.close());
85
+ onTestFinished(() => credentials.close());
90
86
 
91
87
  await queriedCredential.wait();
92
88
  });
@@ -94,7 +90,7 @@ describe('ClientServicesHost', () => {
94
90
  test('sign presentation', async () => {
95
91
  const host = createServiceHost(new Config(), new MemorySignalManagerContext());
96
92
  await host.open(new Context());
97
- afterTest(() => host.close());
93
+ onTestFinished(() => host.close());
98
94
 
99
95
  await host.services.IdentityService!.createIdentity({});
100
96
 
@@ -147,9 +143,9 @@ describe('ClientServicesHost', () => {
147
143
  trigger.wake(identity.identity);
148
144
  }
149
145
  });
150
- await expect(asyncTimeout(trigger.wait(), 200)).to.be.rejectedWith();
146
+ await expect(asyncTimeout(trigger.wait(), 200)).rejects.toBeInstanceOf(Error);
151
147
  await stream?.close();
152
148
  await host.close();
153
149
  }
154
- }).onlyEnvironments('nodejs', 'chromium', 'firefox');
150
+ });
155
151
  });
@@ -6,7 +6,7 @@ import { Event, synchronized } from '@dxos/async';
6
6
  import { clientServiceBundle, type ClientServices } from '@dxos/client-protocol';
7
7
  import { type Config } from '@dxos/config';
8
8
  import { Context } from '@dxos/context';
9
- import { EdgeClient, type EdgeConnection } from '@dxos/edge-client';
9
+ import { EdgeClient, EdgeHttpClient, createStubEdgeIdentity, type EdgeConnection } from '@dxos/edge-client';
10
10
  import { invariant } from '@dxos/invariant';
11
11
  import { PublicKey } from '@dxos/keys';
12
12
  import { type LevelDB } from '@dxos/kv-store';
@@ -15,7 +15,7 @@ import { EdgeSignalManager, WebsocketSignalManager, type SignalManager } from '@
15
15
  import {
16
16
  SwarmNetworkManager,
17
17
  createIceProvider,
18
- createSimplePeerTransportFactory,
18
+ createRtcTransportFactory,
19
19
  type TransportFactory,
20
20
  } from '@dxos/network-manager';
21
21
  import { trace } from '@dxos/protocols';
@@ -26,12 +26,13 @@ import { WebsocketRpcClient } from '@dxos/websocket-rpc';
26
26
 
27
27
  import { ServiceContext, type ServiceContextRuntimeParams } from './service-context';
28
28
  import { ServiceRegistry } from './service-registry';
29
+ import { EdgeAgentServiceImpl } from '../agents';
29
30
  import { DevicesServiceImpl } from '../devices';
30
31
  import { DevtoolsHostEvents, DevtoolsServiceImpl } from '../devtools';
31
32
  import {
32
- type CollectDiagnosticsBroadcastHandler,
33
33
  createCollectDiagnosticsBroadcastHandler,
34
34
  createDiagnostics,
35
+ type CollectDiagnosticsBroadcastHandler,
35
36
  } from '../diagnostics';
36
37
  import { IdentityServiceImpl, type CreateIdentityOptions } from '../identity';
37
38
  import { ContactsServiceImpl } from '../identity/contacts-service';
@@ -89,6 +90,7 @@ export class ClientServicesHost {
89
90
  private _callbacks?: ClientServicesHostCallbacks;
90
91
  private _devtoolsProxy?: WebsocketRpcClient<{}, ClientServices>;
91
92
  private _edgeConnection?: EdgeConnection = undefined;
93
+ private _edgeHttpClient?: EdgeHttpClient = undefined;
92
94
 
93
95
  private _serviceContext!: ServiceContext;
94
96
  private readonly _runtimeParams: ServiceContextRuntimeParams;
@@ -100,6 +102,9 @@ export class ClientServicesHost {
100
102
  @Trace.info()
101
103
  private _open = false;
102
104
 
105
+ @Trace.info()
106
+ private _resetting = false;
107
+
103
108
  constructor({
104
109
  config,
105
110
  transportFactory,
@@ -140,7 +145,7 @@ export class ClientServicesHost {
140
145
  this._systemService = new SystemServiceImpl({
141
146
  config: () => this._config,
142
147
  statusUpdate: this._statusUpdate,
143
- getCurrentStatus: () => (this.isOpen ? SystemStatus.ACTIVE : SystemStatus.INACTIVE),
148
+ getCurrentStatus: () => (this.isOpen && !this._resetting ? SystemStatus.ACTIVE : SystemStatus.INACTIVE),
144
149
  getDiagnostics: () => {
145
150
  return createDiagnostics(this._serviceRegistry.services, this._serviceContext, this._config!);
146
151
  },
@@ -212,13 +217,13 @@ export class ClientServicesHost {
212
217
 
213
218
  const edgeEndpoint = config?.get('runtime.services.edge.url');
214
219
  if (edgeEndpoint) {
215
- const randomKey = PublicKey.random().toHex();
216
- this._edgeConnection = new EdgeClient(randomKey, randomKey, { socketEndpoint: edgeEndpoint });
220
+ this._edgeConnection = new EdgeClient(createStubEdgeIdentity(), { socketEndpoint: edgeEndpoint });
221
+ this._edgeHttpClient = new EdgeHttpClient(edgeEndpoint);
217
222
  }
218
223
 
219
224
  const {
220
225
  connectionLog = true,
221
- transportFactory = createSimplePeerTransportFactory(
226
+ transportFactory = createRtcTransportFactory(
222
227
  { iceServers: this._config?.get('runtime.services.ice') },
223
228
  this._config?.get('runtime.services.iceProviders') &&
224
229
  createIceProvider(this._config!.get('runtime.services.iceProviders')!),
@@ -278,6 +283,7 @@ export class ClientServicesHost {
278
283
  this._networkManager,
279
284
  this._signalManager,
280
285
  this._edgeConnection,
286
+ this._edgeHttpClient,
281
287
  this._runtimeParams,
282
288
  this._config.get('runtime.client.edgeFeatures'),
283
289
  );
@@ -287,8 +293,14 @@ export class ClientServicesHost {
287
293
  return this._serviceContext.dataSpaceManager!;
288
294
  };
289
295
 
296
+ const agentManagerProvider = async () => {
297
+ await this._serviceContext.initialized.wait();
298
+ return this._serviceContext.edgeAgentManager!;
299
+ };
300
+
290
301
  const identityService = new IdentityServiceImpl(
291
302
  this._serviceContext.identityManager,
303
+ this._serviceContext.recoveryManager,
292
304
  this._serviceContext.keyring,
293
305
  () => this._serviceContext.dataSpaceManager!,
294
306
  (params) => this._createIdentity(params),
@@ -328,6 +340,8 @@ export class ClientServicesHost {
328
340
  config: this._config,
329
341
  context: this._serviceContext,
330
342
  }),
343
+
344
+ EdgeAgentService: new EdgeAgentServiceImpl(agentManagerProvider),
331
345
  });
332
346
 
333
347
  await this._serviceContext.open(ctx);
@@ -378,6 +392,10 @@ export class ClientServicesHost {
378
392
  log.trace('dxos.sdk.client-services-host.reset', trace.begin({ id: traceId }));
379
393
 
380
394
  log.info('resetting...');
395
+ // Emit this status update immediately so app returns to fallback.
396
+ // This state is never cleared because the app reloads.
397
+ this._resetting = true;
398
+ this._statusUpdate.emit();
381
399
  await this._serviceContext?.close();
382
400
  await this._storage!.reset();
383
401
  log.info('reset');
@@ -2,7 +2,7 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import { expect } from 'chai';
5
+ import { describe, expect, test } from 'vitest';
6
6
 
7
7
  import { Event } from '@dxos/async';
8
8
  import { type ClientServices } from '@dxos/client-protocol';
@@ -12,7 +12,6 @@ import { log } from '@dxos/log';
12
12
  import { schema } from '@dxos/protocols/proto';
13
13
  import { type SystemService, SystemStatus } from '@dxos/protocols/proto/dxos/client/services';
14
14
  import { createLinkedPorts, createProtoRpcPeer, createServiceBundle } from '@dxos/rpc';
15
- import { describe, test } from '@dxos/test';
16
15
 
17
16
  import { ServiceRegistry } from './service-registry';
18
17
  import { SystemServiceImpl } from '../system';
@@ -2,7 +2,7 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import { expect } from 'chai';
5
+ import { describe, expect, test } from 'vitest';
6
6
 
7
7
  import { asyncTimeout, latch } from '@dxos/async';
8
8
  import { createAdmissionCredentials } from '@dxos/credentials';
@@ -10,7 +10,7 @@ import { AuthStatus } from '@dxos/echo-pipeline';
10
10
  import { writeMessages } from '@dxos/feed-store';
11
11
  import { log } from '@dxos/log';
12
12
  import { SpaceState } from '@dxos/protocols/proto/dxos/client/services';
13
- import { describe, openAndClose, test } from '@dxos/test';
13
+ import { openAndClose } from '@dxos/test-utils';
14
14
 
15
15
  import { TestBuilder, type TestPeer } from '../testing';
16
16