livekit-client 1.12.0 → 1.12.2
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +19 -1
- package/dist/livekit-client.e2ee.worker.js +1 -1
- package/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs +442 -334
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +12303 -14499
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts +2 -2
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts +3 -2
- package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/Checker.d.ts +3 -2
- package/dist/src/connectionHelper/checks/Checker.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/webrtc.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/websocket.d.ts.map +1 -1
- package/dist/src/e2ee/E2eeManager.d.ts +4 -2
- package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
- package/dist/src/e2ee/KeyProvider.d.ts +4 -2
- package/dist/src/e2ee/KeyProvider.d.ts.map +1 -1
- package/dist/src/e2ee/constants.d.ts +1 -0
- package/dist/src/e2ee/constants.d.ts.map +1 -1
- package/dist/src/e2ee/types.d.ts +1 -0
- package/dist/src/e2ee/types.d.ts.map +1 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts +4 -3
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +21 -2
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/proto/livekit_models_pb.d.ts +1264 -0
- package/dist/src/proto/livekit_models_pb.d.ts.map +1 -0
- package/dist/src/proto/livekit_rtc_pb.d.ts +1373 -0
- package/dist/src/proto/livekit_rtc_pb.d.ts.map +1 -0
- package/dist/src/room/PCTransport.d.ts +2 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +9 -5
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/RegionUrlProvider.d.ts +4 -1
- package/dist/src/room/RegionUrlProvider.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +15 -11
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +2 -2
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +5 -3
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/participant/ParticipantTrackPermission.d.ts +1 -1
- package/dist/src/room/participant/ParticipantTrackPermission.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +8 -7
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/timers.d.ts +5 -4
- package/dist/src/room/timers.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +3 -0
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrackPublication.d.ts +1 -1
- package/dist/src/room/track/LocalTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts +2 -2
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts +1 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +6 -4
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/TrackPublication.d.ts +7 -5
- package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/create.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +7 -0
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/track/utils.d.ts +5 -1
- package/dist/src/room/track/utils.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +3 -1
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/test/mocks.d.ts +4 -3
- package/dist/src/test/mocks.d.ts.map +1 -1
- package/dist/src/utils/browserParser.d.ts +2 -0
- package/dist/src/utils/browserParser.d.ts.map +1 -1
- package/dist/ts4.2/src/api/SignalClient.d.ts +2 -2
- package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +3 -2
- package/dist/ts4.2/src/connectionHelper/checks/Checker.d.ts +3 -2
- package/dist/ts4.2/src/e2ee/E2eeManager.d.ts +4 -2
- package/dist/ts4.2/src/e2ee/KeyProvider.d.ts +4 -2
- package/dist/ts4.2/src/e2ee/constants.d.ts +1 -0
- package/dist/ts4.2/src/e2ee/types.d.ts +1 -0
- package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +4 -3
- package/dist/ts4.2/src/e2ee/worker/ParticipantKeyHandler.d.ts +21 -2
- package/dist/ts4.2/src/index.d.ts +1 -1
- package/dist/ts4.2/src/proto/livekit_models_pb.d.ts +1264 -0
- package/dist/ts4.2/src/proto/livekit_rtc_pb.d.ts +1373 -0
- package/dist/ts4.2/src/room/PCTransport.d.ts +2 -1
- package/dist/ts4.2/src/room/RTCEngine.d.ts +9 -5
- package/dist/ts4.2/src/room/RegionUrlProvider.d.ts +4 -1
- package/dist/ts4.2/src/room/Room.d.ts +15 -11
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +2 -2
- package/dist/ts4.2/src/room/participant/Participant.d.ts +5 -3
- package/dist/ts4.2/src/room/participant/ParticipantTrackPermission.d.ts +1 -1
- package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +8 -7
- package/dist/ts4.2/src/room/timers.d.ts +5 -4
- package/dist/ts4.2/src/room/track/LocalTrack.d.ts +3 -0
- package/dist/ts4.2/src/room/track/LocalTrackPublication.d.ts +1 -1
- package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +2 -2
- package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +1 -1
- package/dist/ts4.2/src/room/track/Track.d.ts +6 -4
- package/dist/ts4.2/src/room/track/TrackPublication.d.ts +7 -5
- package/dist/ts4.2/src/room/track/options.d.ts +7 -0
- package/dist/ts4.2/src/room/track/utils.d.ts +5 -1
- package/dist/ts4.2/src/room/utils.d.ts +3 -1
- package/dist/ts4.2/src/test/mocks.d.ts +4 -3
- package/dist/ts4.2/src/utils/browserParser.d.ts +2 -0
- package/package.json +10 -10
- package/src/api/SignalClient.ts +104 -101
- package/src/connectionHelper/ConnectionCheck.ts +3 -2
- package/src/connectionHelper/checks/Checker.ts +3 -3
- package/src/connectionHelper/checks/webrtc.ts +66 -2
- package/src/connectionHelper/checks/websocket.ts +4 -0
- package/src/e2ee/E2eeManager.ts +4 -3
- package/src/e2ee/KeyProvider.ts +3 -2
- package/src/e2ee/constants.ts +4 -0
- package/src/e2ee/types.ts +1 -0
- package/src/e2ee/worker/FrameCryptor.test.ts +1 -3
- package/src/e2ee/worker/FrameCryptor.ts +14 -16
- package/src/e2ee/worker/ParticipantKeyHandler.ts +48 -2
- package/src/e2ee/worker/e2ee.worker.ts +12 -6
- package/src/index.ts +1 -1
- package/src/proto/livekit_models_pb.ts +2096 -0
- package/src/proto/livekit_rtc_pb.ts +2332 -0
- package/src/room/PCTransport.ts +1 -1
- package/src/room/RTCEngine.ts +24 -18
- package/src/room/RegionUrlProvider.ts +11 -2
- package/src/room/Room.test.ts +1 -0
- package/src/room/Room.ts +175 -86
- package/src/room/participant/LocalParticipant.ts +43 -59
- package/src/room/participant/Participant.ts +6 -4
- package/src/room/participant/ParticipantTrackPermission.ts +3 -3
- package/src/room/participant/RemoteParticipant.ts +24 -21
- package/src/room/participant/publishUtils.test.ts +1 -0
- package/src/room/track/LocalTrack.ts +24 -9
- package/src/room/track/LocalTrackPublication.ts +1 -1
- package/src/room/track/LocalVideoTrack.test.ts +2 -1
- package/src/room/track/LocalVideoTrack.ts +22 -22
- package/src/room/track/RemoteTrackPublication.ts +12 -7
- package/src/room/track/RemoteVideoTrack.test.ts +5 -4
- package/src/room/track/Track.ts +9 -6
- package/src/room/track/TrackPublication.ts +7 -5
- package/src/room/track/create.ts +18 -17
- package/src/room/track/facingMode.test.ts +1 -0
- package/src/room/track/options.ts +6 -0
- package/src/room/track/utils.test.ts +1 -0
- package/src/room/track/utils.ts +44 -2
- package/src/room/utils.test.ts +16 -0
- package/src/room/utils.ts +20 -4
- package/src/test/mocks.ts +7 -5
- package/src/utils/AsyncQueue.test.ts +1 -0
- package/src/utils/browserParser.test.ts +33 -3
- package/src/utils/browserParser.ts +5 -0
- package/dist/src/proto/google/protobuf/timestamp.d.ts +0 -146
- package/dist/src/proto/google/protobuf/timestamp.d.ts.map +0 -1
- package/dist/src/proto/livekit_models.d.ts +0 -2399
- package/dist/src/proto/livekit_models.d.ts.map +0 -1
- package/dist/src/proto/livekit_rtc.d.ts +0 -14352
- package/dist/src/proto/livekit_rtc.d.ts.map +0 -1
- package/dist/ts4.2/src/proto/google/protobuf/timestamp.d.ts +0 -150
- package/dist/ts4.2/src/proto/livekit_models.d.ts +0 -2659
- package/dist/ts4.2/src/proto/livekit_rtc.d.ts +0 -15764
- package/src/proto/google/protobuf/timestamp.ts +0 -230
- package/src/proto/livekit_models.ts +0 -4006
- package/src/proto/livekit_rtc.ts +0 -4672
@@ -1,6 +1,7 @@
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
2
2
|
// TODO code inspired by https://github.com/webrtc/samples/blob/gh-pages/src/content/insertable-streams/endtoend-encryption/js/worker.js
|
3
|
-
import EventEmitter from '
|
3
|
+
import { EventEmitter } from 'events';
|
4
|
+
import type TypedEventEmitter from 'typed-emitter';
|
4
5
|
import { workerLogger } from '../../logger';
|
5
6
|
import type { VideoCodec } from '../../room/track/options';
|
6
7
|
import { ENCRYPTION_ALGORITHM, IV_LENGTH, UNENCRYPTED_BYTES } from '../constants';
|
@@ -26,7 +27,7 @@ export interface TransformerInfo {
|
|
26
27
|
abortController: AbortController;
|
27
28
|
}
|
28
29
|
|
29
|
-
export class BaseFrameCryptor extends EventEmitter<CryptorCallbacks> {
|
30
|
+
export class BaseFrameCryptor extends (EventEmitter as new () => TypedEventEmitter<CryptorCallbacks>) {
|
30
31
|
encodeFunction(
|
31
32
|
encodedFrame: RTCEncodedVideoFrame | RTCEncodedAudioFrame,
|
32
33
|
controller: TransformStreamDefaultController,
|
@@ -49,8 +50,6 @@ export class BaseFrameCryptor extends EventEmitter<CryptorCallbacks> {
|
|
49
50
|
export class FrameCryptor extends BaseFrameCryptor {
|
50
51
|
private sendCounts: Map<number, number>;
|
51
52
|
|
52
|
-
private isKeyInvalid = false;
|
53
|
-
|
54
53
|
private participantId: string | undefined;
|
55
54
|
|
56
55
|
private trackId: string | undefined;
|
@@ -270,16 +269,16 @@ export class FrameCryptor extends BaseFrameCryptor {
|
|
270
269
|
const data = new Uint8Array(encodedFrame.data);
|
271
270
|
const keyIndex = data[encodedFrame.data.byteLength - 1];
|
272
271
|
|
273
|
-
if (this.keys.getKeySet(keyIndex)) {
|
272
|
+
if (this.keys.getKeySet(keyIndex) && this.keys.hasValidKey) {
|
274
273
|
try {
|
275
274
|
const decodedFrame = await this.decryptFrame(encodedFrame, keyIndex);
|
275
|
+
this.keys.decryptionSuccess();
|
276
276
|
if (decodedFrame) {
|
277
277
|
return controller.enqueue(decodedFrame);
|
278
278
|
}
|
279
|
-
this.isKeyInvalid = false;
|
280
279
|
} catch (error) {
|
281
280
|
if (error instanceof CryptorError && error.reason === CryptorErrorReason.InvalidKey) {
|
282
|
-
if (
|
281
|
+
if (this.keys.hasValidKey) {
|
283
282
|
workerLogger.warn('invalid key');
|
284
283
|
this.emit(
|
285
284
|
CryptorEvent.Error,
|
@@ -288,20 +287,12 @@ export class FrameCryptor extends BaseFrameCryptor {
|
|
288
287
|
CryptorErrorReason.InvalidKey,
|
289
288
|
),
|
290
289
|
);
|
291
|
-
this.
|
290
|
+
this.keys.decryptionFailure();
|
292
291
|
}
|
293
292
|
} else {
|
294
293
|
workerLogger.warn('decoding frame failed', { error });
|
295
294
|
}
|
296
295
|
}
|
297
|
-
} else {
|
298
|
-
this.emit(
|
299
|
-
CryptorEvent.Error,
|
300
|
-
new CryptorError(
|
301
|
-
`key missing for participant ${this.participantId}`,
|
302
|
-
CryptorErrorReason.MissingKey,
|
303
|
-
),
|
304
|
-
);
|
305
296
|
}
|
306
297
|
|
307
298
|
return controller.enqueue(encodedFrame);
|
@@ -407,6 +398,13 @@ export class FrameCryptor extends BaseFrameCryptor {
|
|
407
398
|
}
|
408
399
|
|
409
400
|
workerLogger.warn('maximum ratchet attempts exceeded, resetting key');
|
401
|
+
this.emit(
|
402
|
+
CryptorEvent.Error,
|
403
|
+
new CryptorError(
|
404
|
+
`valid key missing for participant ${this.participantId}`,
|
405
|
+
CryptorErrorReason.MissingKey,
|
406
|
+
),
|
407
|
+
);
|
410
408
|
}
|
411
409
|
} else {
|
412
410
|
throw new CryptorError(
|
@@ -1,4 +1,5 @@
|
|
1
|
-
import EventEmitter from '
|
1
|
+
import { EventEmitter } from 'events';
|
2
|
+
import type TypedEventEmitter from 'typed-emitter';
|
2
3
|
import { workerLogger } from '../../logger';
|
3
4
|
import { KEYRING_SIZE } from '../constants';
|
4
5
|
import type { KeyProviderOptions, KeySet, ParticipantKeyHandlerCallbacks } from '../types';
|
@@ -15,7 +16,7 @@ import { deriveKeys, importKey, ratchet } from '../utils';
|
|
15
16
|
* if decryption fails or can be triggered manually on both sender and receiver side.
|
16
17
|
*
|
17
18
|
*/
|
18
|
-
export class ParticipantKeyHandler extends EventEmitter<ParticipantKeyHandlerCallbacks> {
|
19
|
+
export class ParticipantKeyHandler extends (EventEmitter as new () => TypedEventEmitter<ParticipantKeyHandlerCallbacks>) {
|
19
20
|
private currentKeyIndex: number;
|
20
21
|
|
21
22
|
private cryptoKeyRing: Array<KeySet>;
|
@@ -28,6 +29,14 @@ export class ParticipantKeyHandler extends EventEmitter<ParticipantKeyHandlerCal
|
|
28
29
|
|
29
30
|
private participantId: string | undefined;
|
30
31
|
|
32
|
+
private decryptionFailureCount = 0;
|
33
|
+
|
34
|
+
private _hasValidKey: boolean = true;
|
35
|
+
|
36
|
+
get hasValidKey() {
|
37
|
+
return this._hasValidKey;
|
38
|
+
}
|
39
|
+
|
31
40
|
constructor(
|
32
41
|
participantId: string | undefined,
|
33
42
|
isEnabled: boolean,
|
@@ -40,12 +49,37 @@ export class ParticipantKeyHandler extends EventEmitter<ParticipantKeyHandlerCal
|
|
40
49
|
this.keyProviderOptions = keyProviderOptions;
|
41
50
|
this.ratchetPromiseMap = new Map();
|
42
51
|
this.participantId = participantId;
|
52
|
+
this.resetKeyStatus();
|
43
53
|
}
|
44
54
|
|
45
55
|
setEnabled(enabled: boolean) {
|
46
56
|
this.enabled = enabled;
|
47
57
|
}
|
48
58
|
|
59
|
+
decryptionFailure() {
|
60
|
+
if (this.keyProviderOptions.failureTolerance < 0) {
|
61
|
+
return;
|
62
|
+
}
|
63
|
+
this.decryptionFailureCount += 1;
|
64
|
+
|
65
|
+
if (this.decryptionFailureCount > this.keyProviderOptions.failureTolerance) {
|
66
|
+
this._hasValidKey = false;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
decryptionSuccess() {
|
71
|
+
this.resetKeyStatus();
|
72
|
+
}
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Call this after user initiated ratchet or a new key has been set in order to make sure to mark potentially
|
76
|
+
* invalid keys as valid again
|
77
|
+
*/
|
78
|
+
resetKeyStatus() {
|
79
|
+
this.decryptionFailureCount = 0;
|
80
|
+
this._hasValidKey = true;
|
81
|
+
}
|
82
|
+
|
49
83
|
/**
|
50
84
|
* Ratchets the current key (or the one at keyIndex if provided) and
|
51
85
|
* returns the ratcheted material
|
@@ -84,6 +118,17 @@ export class ParticipantKeyHandler extends EventEmitter<ParticipantKeyHandlerCal
|
|
84
118
|
return ratchetPromise;
|
85
119
|
}
|
86
120
|
|
121
|
+
/**
|
122
|
+
* takes in a key material with `deriveBits` and `deriveKey` set as key usages
|
123
|
+
* and derives encryption keys from the material and sets it on the key ring buffer
|
124
|
+
* together with the material
|
125
|
+
* also resets the valid key property and updates the currentKeyIndex
|
126
|
+
*/
|
127
|
+
async setKey(material: CryptoKey, keyIndex = 0) {
|
128
|
+
await this.setKeyFromMaterial(material, keyIndex);
|
129
|
+
this.resetKeyStatus();
|
130
|
+
}
|
131
|
+
|
87
132
|
/**
|
88
133
|
* takes in a key material with `deriveBits` and `deriveKey` set as key usages
|
89
134
|
* and derives encryption keys from the material and sets it on the key ring buffer
|
@@ -108,6 +153,7 @@ export class ParticipantKeyHandler extends EventEmitter<ParticipantKeyHandlerCal
|
|
108
153
|
|
109
154
|
async setCurrentKeyIndex(index: number) {
|
110
155
|
this.currentKeyIndex = index % this.cryptoKeyRing.length;
|
156
|
+
this.resetKeyStatus();
|
111
157
|
}
|
112
158
|
|
113
159
|
isEnabled() {
|
@@ -7,6 +7,7 @@ import type {
|
|
7
7
|
ErrorMessage,
|
8
8
|
KeyProviderOptions,
|
9
9
|
RatchetMessage,
|
10
|
+
RatchetRequestMessage,
|
10
11
|
} from '../types';
|
11
12
|
import { FrameCryptor } from './FrameCryptor';
|
12
13
|
import { ParticipantKeyHandler } from './ParticipantKeyHandler';
|
@@ -75,7 +76,7 @@ onmessage = (ev) => {
|
|
75
76
|
workerLogger.debug('set shared key');
|
76
77
|
setSharedKey(data.key, data.keyIndex);
|
77
78
|
} else if (data.participantId) {
|
78
|
-
getParticipantKeyHandler(data.participantId).
|
79
|
+
getParticipantKeyHandler(data.participantId).setKey(data.key, data.keyIndex);
|
79
80
|
} else {
|
80
81
|
workerLogger.error('no participant Id was provided and shared key usage is disabled');
|
81
82
|
}
|
@@ -92,13 +93,18 @@ onmessage = (ev) => {
|
|
92
93
|
});
|
93
94
|
break;
|
94
95
|
case 'ratchetRequest':
|
95
|
-
|
96
|
-
|
96
|
+
handleRatchetRequest(data);
|
97
97
|
default:
|
98
98
|
break;
|
99
99
|
}
|
100
100
|
};
|
101
101
|
|
102
|
+
async function handleRatchetRequest(data: RatchetRequestMessage['data']) {
|
103
|
+
const keyHandler = getParticipantKeyHandler(data.participantId);
|
104
|
+
await keyHandler.ratchetKey(data.keyIndex);
|
105
|
+
keyHandler.resetKeyStatus();
|
106
|
+
}
|
107
|
+
|
102
108
|
function getTrackCryptor(participantId: string, trackId: string) {
|
103
109
|
let cryptor = participantCryptors.find((c) => c.getTrackId() === trackId);
|
104
110
|
if (!cryptor) {
|
@@ -131,7 +137,7 @@ function getParticipantKeyHandler(participantId?: string) {
|
|
131
137
|
if (!keys) {
|
132
138
|
keys = new ParticipantKeyHandler(participantId, true, keyProviderOptions);
|
133
139
|
if (sharedKey) {
|
134
|
-
keys.
|
140
|
+
keys.setKey(sharedKey);
|
135
141
|
}
|
136
142
|
participantKeys.set(participantId, keys);
|
137
143
|
}
|
@@ -171,9 +177,9 @@ function setEncryptionEnabled(enable: boolean, participantId?: string) {
|
|
171
177
|
function setSharedKey(key: CryptoKey, index?: number) {
|
172
178
|
workerLogger.debug('setting shared key');
|
173
179
|
sharedKey = key;
|
174
|
-
publisherKeys?.
|
180
|
+
publisherKeys?.setKey(key, index);
|
175
181
|
for (const [, keyHandler] of participantKeys) {
|
176
|
-
keyHandler.
|
182
|
+
keyHandler.setKey(key, index);
|
177
183
|
}
|
178
184
|
}
|
179
185
|
|
package/src/index.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { LogLevel, setLogExtension, setLogLevel } from './logger';
|
2
|
-
import { DataPacket_Kind, DisconnectReason, VideoQuality } from './proto/
|
2
|
+
import { DataPacket_Kind, DisconnectReason, VideoQuality } from './proto/livekit_models_pb';
|
3
3
|
import DefaultReconnectPolicy from './room/DefaultReconnectPolicy';
|
4
4
|
import Room, { ConnectionState, RoomState } from './room/Room';
|
5
5
|
import LocalParticipant from './room/participant/LocalParticipant';
|