reploid 0.0.0-stage → 0.1.0
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.
- package/apps/poolday/config/chat-files.json +27 -0
- package/apps/poolday/config/partition-generation.js +21 -0
- package/apps/poolday/config/partition-policy.json +74 -0
- package/apps/poolday/config/partition-releases.json +3 -0
- package/apps/poolday/config/work-profile.json +122 -0
- package/apps/poolday/host/chat-view.js +47 -0
- package/apps/poolday/host/earning/client.js +199 -0
- package/apps/poolday/host/earning/contribution.js +168 -0
- package/apps/poolday/host/partition-release.js +33 -0
- package/apps/poolday/host/work-partition-artifact-source.js +37 -0
- package/apps/poolday/host/work-partition-capsule.js +39 -0
- package/apps/poolday/host/work-partition-plan.js +167 -0
- package/apps/poolday/host/work-partition-tokenizer-guard.js +21 -0
- package/apps/poolday/infrastructure/model-storage-lock.js +22 -0
- package/apps/poolday/infrastructure/pack-release-storage.js +52 -0
- package/apps/poolday/pool/pack-operation.js +161 -0
- package/apps/poolday/pool/pack-release-policy.js +44 -0
- package/apps/poolday/pool/peer-pack-data-channel.js +217 -0
- package/apps/poolday/providers/work-adapter.js +67 -0
- package/apps/poolday/providers/work-device.js +33 -0
- package/apps/poolday/providers/work-generation-completion.js +55 -0
- package/apps/poolday/providers/work-provider.js +209 -0
- package/apps/poolday/providers/work-resident-provider.js +113 -0
- package/deploy/artifacts/doppler-gpu-0.7.1.tgz +0 -0
- package/docs/fleet-service.md +214 -0
- package/docs/native-participant.md +534 -0
- package/native-delivery.json +1174 -0
- package/package.json +29 -4
- package/packages/reploid/src/artifacts/binary.js +25 -0
- package/packages/reploid/src/artifacts/canonical-json.js +23 -0
- package/packages/reploid/src/artifacts/custody/exchange.js +402 -0
- package/packages/reploid/src/artifacts/custody/piece-acquisition.js +70 -0
- package/packages/reploid/src/artifacts/custody/runtime.js +404 -0
- package/packages/reploid/src/artifacts/custody/supply-budget.js +19 -0
- package/packages/reploid/src/artifacts/custody-certificate.js +123 -0
- package/packages/reploid/src/artifacts/delivery-attestation.js +64 -0
- package/packages/reploid/src/artifacts/generation-terminal.js +36 -0
- package/packages/reploid/src/artifacts/identity.js +91 -0
- package/packages/reploid/src/artifacts/participant-proofs.js +159 -0
- package/packages/reploid/src/artifacts/receipt.js +131 -0
- package/packages/reploid/src/artifacts/signatures.js +94 -0
- package/packages/reploid/src/artifacts/whole-model-receipt.js +42 -0
- package/packages/reploid/src/cancellation.js +44 -0
- package/packages/reploid/src/config/defaults.json +110 -0
- package/packages/reploid/src/config/index.js +209 -0
- package/packages/reploid/src/config/request-overrides.json +9 -0
- package/packages/reploid/src/config/transport-defaults.json +7 -0
- package/packages/reploid/src/hashing.js +30 -0
- package/packages/reploid/src/measurement/capacity-utilization.js +321 -0
- package/packages/reploid/src/measurement/index.js +5 -0
- package/packages/reploid/src/measurement/readiness-records.js +310 -0
- package/packages/reploid/src/mesh/computer-identity.js +50 -0
- package/packages/reploid/src/mesh/computer-identity.policy.json +8 -0
- package/packages/reploid/src/mesh/contribution.js +141 -0
- package/packages/reploid/src/mesh/generation-recovery.js +58 -0
- package/packages/reploid/src/mesh/gpu-signature-shader.js +27 -0
- package/packages/reploid/src/mesh/gpu-signature.js +136 -0
- package/packages/reploid/src/mesh/gpu-signature.policy.json +11 -0
- package/packages/reploid/src/mesh/index.js +66 -0
- package/packages/reploid/src/mesh/partitions/automatic-ordered-partitions.js +540 -0
- package/packages/reploid/src/mesh/partitions/automatic-partitions.js +298 -0
- package/packages/reploid/src/mesh/partitions/partition-attempt-evidence.js +282 -0
- package/packages/reploid/src/mesh/partitions/partition-attempt-queue-policy.json +6 -0
- package/packages/reploid/src/mesh/partitions/partition-attempt-queue.js +112 -0
- package/packages/reploid/src/mesh/partitions/partition-canonical-contract.js +167 -0
- package/packages/reploid/src/mesh/partitions/partition-canonical-execution.js +168 -0
- package/packages/reploid/src/mesh/partitions/partition-chat-canonical.js +49 -0
- package/packages/reploid/src/mesh/partitions/partition-chat.js +125 -0
- package/packages/reploid/src/mesh/partitions/partition-contract.js +192 -0
- package/packages/reploid/src/mesh/partitions/partition-device-admission.js +96 -0
- package/packages/reploid/src/mesh/partitions/partition-discovery.js +96 -0
- package/packages/reploid/src/mesh/partitions/partition-entry-publication.js +183 -0
- package/packages/reploid/src/mesh/partitions/partition-entry.js +434 -0
- package/packages/reploid/src/mesh/partitions/partition-generation-request.js +33 -0
- package/packages/reploid/src/mesh/partitions/partition-grants.js +277 -0
- package/packages/reploid/src/mesh/partitions/partition-link.js +103 -0
- package/packages/reploid/src/mesh/partitions/partition-network.js +109 -0
- package/packages/reploid/src/mesh/partitions/partition-ordered-chat.js +290 -0
- package/packages/reploid/src/mesh/partitions/partition-ordered-peer.js +415 -0
- package/packages/reploid/src/mesh/partitions/partition-ordered-publication.js +100 -0
- package/packages/reploid/src/mesh/partitions/partition-peer.js +314 -0
- package/packages/reploid/src/mesh/partitions/partition-placement.js +143 -0
- package/packages/reploid/src/mesh/partitions/partition-preparation-progress.js +25 -0
- package/packages/reploid/src/mesh/partitions/partition-receiver-policy.json +7 -0
- package/packages/reploid/src/mesh/partitions/partition-reservations.js +75 -0
- package/packages/reploid/src/mesh/partitions/partition-route-envelope-policy.json +16 -0
- package/packages/reploid/src/mesh/partitions/partition-route-envelope.js +74 -0
- package/packages/reploid/src/mesh/partitions/partition-route-execution.js +409 -0
- package/packages/reploid/src/mesh/partitions/partition-route-network.js +179 -0
- package/packages/reploid/src/mesh/partitions/partition-route-ranking.js +268 -0
- package/packages/reploid/src/mesh/partitions/partition-runner.js +319 -0
- package/packages/reploid/src/mesh/partitions/partition-stage-assignment.js +109 -0
- package/packages/reploid/src/mesh/partitions/partition-stage-commit.js +103 -0
- package/packages/reploid/src/mesh/partitions/partition-step-receiver.js +291 -0
- package/packages/reploid/src/mesh/partitions/partition-summary.js +122 -0
- package/packages/reploid/src/mesh/partitions/preparation-owner.js +40 -0
- package/packages/reploid/src/mesh/partitions/resident-error-codes.json +10 -0
- package/packages/reploid/src/mesh/partitions/resident-partition.js +333 -0
- package/packages/reploid/src/mesh/partitions/resident-route.js +46 -0
- package/packages/reploid/src/mesh/partitions/signed-resident-factory.js +59 -0
- package/packages/reploid/src/mesh/peer-generation.js +896 -0
- package/packages/reploid/src/mesh/peer-identity.js +87 -0
- package/packages/reploid/src/mesh/placement-beliefs.js +107 -0
- package/packages/reploid/src/mesh/preparation-progress.js +46 -0
- package/packages/reploid/src/mesh/provider-hardware.js +11 -0
- package/packages/reploid/src/mesh/remote-generation-requests.js +53 -0
- package/packages/reploid/src/mesh/swarm-coordination.js +140 -0
- package/packages/reploid/src/mesh/web-access-network.js +112 -0
- package/packages/reploid/src/mesh/web-access-policy.json +17 -0
- package/packages/reploid/src/retrieval/citations.js +156 -0
- package/packages/reploid/src/retrieval/claim-support.js +113 -0
- package/packages/reploid/src/retrieval/costs.js +101 -0
- package/packages/reploid/src/retrieval/fixed-provider.js +34 -0
- package/packages/reploid/src/retrieval/index.js +20 -0
- package/packages/reploid/src/retrieval/lexical-support.js +27 -0
- package/packages/reploid/src/retrieval/model-reviewer.js +67 -0
- package/packages/reploid/src/retrieval/page-fetch.json +10 -0
- package/packages/reploid/src/retrieval/page-text.js +182 -0
- package/packages/reploid/src/retrieval/passage.js +37 -0
- package/packages/reploid/src/retrieval/policy.json +14 -0
- package/packages/reploid/src/retrieval/retrieval.js +23 -0
- package/packages/reploid/src/retrieval/review-queue.js +76 -0
- package/packages/reploid/src/retrieval/sources.js +263 -0
- package/packages/reploid/src/retrieval/support-review.js +155 -0
- package/packages/reploid/src/retrieval/text.js +21 -0
- package/packages/reploid/src/rules/contribution.rules.json +8 -0
- package/packages/reploid/src/rules/routing.rules.json +24 -0
- package/packages/reploid/src/transport/assignment.js +571 -0
- package/packages/reploid/src/transport/bounded-channel-writer.js +46 -0
- package/packages/reploid/src/transport/index.js +11 -0
- package/packages/reploid/src/transport/introduction-admission.js +24 -0
- package/packages/reploid/src/transport/introduction-bootstrap.js +112 -0
- package/packages/reploid/src/transport/introduction-policy.json +12 -0
- package/packages/reploid/src/transport/introduction-records.js +90 -0
- package/packages/reploid/src/transport/introductions.js +209 -0
- package/packages/reploid/src/transport/partition-data-channel.js +421 -0
- package/packages/reploid/src/transport/partition-errors.json +16 -0
- package/packages/reploid/src/transport/retry-policy.js +36 -0
- package/packages/reploid/src/transport/room.js +502 -0
- package/packages/reploid/src/transport/sdp-fingerprint.js +5 -0
- package/packages/reploid/src/transport/signaling.js +571 -0
- package/packages/reploid/src/transport/swarm.js +1247 -0
- package/packages/reploid/src/transport/tool-offer-channel.js +137 -0
- package/server/browser-roots.js +42 -0
- package/server/browser-web-worker.js +94 -0
- package/server/chat-completions.js +45 -0
- package/server/fleet/browser-bridge.js +145 -0
- package/server/fleet/contract.js +42 -0
- package/server/fleet/credit-evidence.js +103 -0
- package/server/fleet/credit-recovery.js +175 -0
- package/server/fleet/credits.js +178 -0
- package/server/fleet/execution-admission.js +32 -0
- package/server/fleet/native-host.js +817 -0
- package/server/fleet/placement.js +132 -0
- package/server/fleet/requester.js +346 -0
- package/server/fleet/service.js +311 -0
- package/server/fleet/terminal-policy.js +83 -0
- package/server/fleet/text-api.js +94 -0
- package/server/hashing.js +35 -0
- package/server/http-request-owner.js +49 -0
- package/server/messages.js +57 -0
- package/server/native-canonical-epochs.js +90 -0
- package/server/native-change-log.js +40 -0
- package/server/native-earning.js +203 -0
- package/server/native-generation-journal.js +46 -0
- package/server/native-model-config.js +57 -0
- package/server/native-model-files.js +382 -0
- package/server/native-model-origin.js +54 -0
- package/server/native-model-origins.json +12 -0
- package/server/native-node.js +83 -0
- package/server/native-owner/controls.js +162 -0
- package/server/native-owner/monitor.js +251 -0
- package/server/native-owner/policy.js +273 -0
- package/server/native-owner/rate.js +92 -0
- package/server/native-participant.js +866 -0
- package/server/native-readiness-log.js +30 -0
- package/server/native-release-storage.js +64 -0
- package/server/native-shader-cache.js +369 -0
- package/server/native-supply-budget.js +66 -0
- package/server/native-whole-earning.js +67 -0
- package/server/native-whole-model-fill.js +182 -0
- package/server/native-whole-model.js +130 -0
- package/server/public-web-worker.js +66 -0
- package/server/responses.js +92 -0
- package/server/search/errors.js +47 -0
- package/server/search/page-fetch.js +185 -0
- package/server/text-api.js +83 -0
- package/server/text-recovery.js +45 -0
- package/server/text-request-options.js +23 -0
- package/server/text-requester.js +231 -0
- package/server/token-auth.js +9 -0
- package/server/whole-model-requester.js +99 -0
- package/shared/config/chat-models.json +4685 -0
- package/shared/config/doppler-execution-contracts.js +32 -0
- package/shared/config/doppler-execution-contracts.json +43 -0
- package/shared/config/doppler-generation-contract.js +223 -0
- package/shared/config/doppler-local-models.js +79 -0
- package/shared/config/doppler-package.json +7 -0
- package/shared/config/model-manifests/a0dc69e917133a82780823d17fc771c0f6b882d7c86c571deeaef45be2ff108b.json +6434 -0
- package/shared/config/model-manifests/e985e3cdad60d0cb57d84b436161e66ab5b3f6589a168de6f00b4610e5f1fb59.json +16534 -0
- package/shared/config/model-manifests/fa6eb7bbc3ba63571f8cede780b877d9272bde948697af93dd3d33837cb46137.json +7175 -0
- package/shared/config/model-pieces/qwen-0-8b-pieces.json +1506 -0
- package/shared/config/model-pieces/qwen-2b-pieces.json +4800 -0
- package/shared/config/model-pieces/qwen35b-q4_0-pieces.json +28880 -0
- package/shared/config/reploid-library.json +49 -0
- package/shared/config/swarm-bootstrap.json +21 -0
- package/shared/infrastructure/doppler-runtime-service.js +261 -0
- package/shared/pool/executable-pack.js +87 -0
- package/shared/pool/pack-operation-adapters.js +124 -0
- package/shared/pool/pack-operation-policy.js +75 -0
- package/shared/pool/pool-config.json +2280 -0
- package/shared/vendor/reploid/adapters/doppler-session.js +372 -0
- package/shared/vendor/reploid/artifacts/binary.js +25 -0
- package/shared/vendor/reploid/artifacts/canonical-json.js +23 -0
- package/shared/vendor/reploid/artifacts/delivery-attestation.js +64 -0
- package/shared/vendor/reploid/artifacts/identity.js +91 -0
- package/shared/vendor/reploid/artifacts/manifest-identity.js +17 -0
- package/shared/vendor/reploid/artifacts/participant-proofs.js +159 -0
- package/shared/vendor/reploid/artifacts/signatures.js +94 -0
- package/shared/vendor/reploid/config/defaults.json +110 -0
- package/shared/vendor/reploid/config/index.js +209 -0
- package/shared/vendor/reploid/config/request-overrides.json +9 -0
- package/shared/vendor/reploid/hashing.js +30 -0
- package/shared/vendor/reploid/mesh/generation-context-cache.js +97 -0
- package/shared/vendor/reploid/mesh/partitions/partition-attempt-evidence.js +282 -0
- package/shared/vendor/reploid/mesh/partitions/partition-contract.js +192 -0
- package/shared/vendor/reploid/mesh/partitions/partition-receiver-policy.json +7 -0
- package/shared/vendor/reploid/mesh/partitions/partition-route-ranking.js +268 -0
- package/shared/vendor/reploid/mesh/preparation-progress.js +46 -0
- package/shared/vendor/reploid/mesh/provider-hardware.js +11 -0
- package/shared/vendor/reploid/rules/routing.rules.json +24 -0
- package/shared/vendor/reploid/transport/partition-errors.json +16 -0
- package/README.md +0 -3
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
import { requireResolvedConfig } from '../config/index.js';
|
|
2
|
+
import defaults from '../config/transport-defaults.json' with { type: 'json' };
|
|
3
|
+
import { boundedRetryDelay, retryAfterMsFromError } from './retry-policy.js';
|
|
4
|
+
|
|
5
|
+
export const SIGNAL_TYPES = Object.freeze({
|
|
6
|
+
OFFER: 'offer',
|
|
7
|
+
ANSWER: 'answer',
|
|
8
|
+
ICE_CANDIDATE: 'ice-candidate',
|
|
9
|
+
CLOSE: 'close',
|
|
10
|
+
PING: 'ping',
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const DEFAULT_SIGNAL_POLL_TIMEOUT_MS = defaults.DEFAULT_SIGNAL_POLL_TIMEOUT_MS;
|
|
14
|
+
export const DEFAULT_SIGNAL_FAILURE_THRESHOLD = defaults.DEFAULT_SIGNAL_FAILURE_THRESHOLD;
|
|
15
|
+
export const DEFAULT_SIGNAL_POLL_BACKOFF_BASE_MS = defaults.DEFAULT_SIGNAL_POLL_BACKOFF_BASE_MS;
|
|
16
|
+
export const DEFAULT_SIGNAL_POLL_BACKOFF_MAX_MS = defaults.DEFAULT_SIGNAL_POLL_BACKOFF_MAX_MS;
|
|
17
|
+
|
|
18
|
+
export function createSignalId(prefix = 'sig') {
|
|
19
|
+
if (globalThis.crypto?.randomUUID) {
|
|
20
|
+
return `${prefix}_${globalThis.crypto.randomUUID()}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const random = Math.random().toString(36).slice(2);
|
|
24
|
+
return `${prefix}_${Date.now().toString(36)}_${random}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createSignalMessage({
|
|
28
|
+
id = createSignalId(),
|
|
29
|
+
sessionId,
|
|
30
|
+
assignmentId = null,
|
|
31
|
+
type,
|
|
32
|
+
fromPeerId,
|
|
33
|
+
toPeerId = null,
|
|
34
|
+
payload = null,
|
|
35
|
+
createdAt = Date.now(),
|
|
36
|
+
expiresAt = null,
|
|
37
|
+
} = {}) {
|
|
38
|
+
const message = {
|
|
39
|
+
id,
|
|
40
|
+
sessionId,
|
|
41
|
+
assignmentId,
|
|
42
|
+
type,
|
|
43
|
+
fromPeerId,
|
|
44
|
+
toPeerId,
|
|
45
|
+
payload,
|
|
46
|
+
createdAt,
|
|
47
|
+
expiresAt,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return normalizeSignalMessage(message);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function normalizeSignalMessage(value) {
|
|
54
|
+
const message = unwrapSignalRecord(value);
|
|
55
|
+
|
|
56
|
+
if (!isPlainObject(message)) {
|
|
57
|
+
throw new TypeError('signal message must be an object');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const normalized = {
|
|
61
|
+
id: requireString(message.id, 'signal message id'),
|
|
62
|
+
sessionId: requireString(message.sessionId, 'signal sessionId'),
|
|
63
|
+
assignmentId: optionalString(message.assignmentId, 'signal assignmentId'),
|
|
64
|
+
type: requireSignalType(message.type),
|
|
65
|
+
fromPeerId: requireString(message.fromPeerId, 'signal fromPeerId'),
|
|
66
|
+
toPeerId: optionalString(message.toPeerId, 'signal toPeerId'),
|
|
67
|
+
payload: message.payload ?? null,
|
|
68
|
+
createdAt: requireFiniteNumber(message.createdAt, 'signal createdAt'),
|
|
69
|
+
expiresAt: optionalFiniteNumber(message.expiresAt, 'signal expiresAt'),
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return Object.freeze(normalized);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function isSignalForPeer(message, {
|
|
76
|
+
sessionId,
|
|
77
|
+
localPeerId,
|
|
78
|
+
remotePeerId = null,
|
|
79
|
+
includeOwnSignals = false,
|
|
80
|
+
now = Date.now(),
|
|
81
|
+
} = {}) {
|
|
82
|
+
const normalized = normalizeSignalMessage(message);
|
|
83
|
+
|
|
84
|
+
if (sessionId && normalized.sessionId !== sessionId) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (normalized.expiresAt !== null && normalized.expiresAt <= now) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!includeOwnSignals && normalized.fromPeerId === localPeerId) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (normalized.toPeerId && normalized.toPeerId !== localPeerId) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (remotePeerId && normalized.fromPeerId !== remotePeerId) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function createCallbackSignalingAdapter({
|
|
108
|
+
publish,
|
|
109
|
+
subscribe,
|
|
110
|
+
close = null,
|
|
111
|
+
} = {}) {
|
|
112
|
+
if (typeof publish !== 'function') {
|
|
113
|
+
throw new TypeError('publish must be a function');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (typeof subscribe !== 'function') {
|
|
117
|
+
throw new TypeError('subscribe must be a function');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return Object.freeze({
|
|
121
|
+
publish(message) {
|
|
122
|
+
return publish(normalizeSignalMessage(message));
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
subscribe(onMessage) {
|
|
126
|
+
if (typeof onMessage !== 'function') {
|
|
127
|
+
throw new TypeError('onMessage must be a function');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return normalizeUnsubscribe(subscribe((record) => {
|
|
131
|
+
for (const candidate of extractSignalRecords(record)) {
|
|
132
|
+
onMessage(normalizeSignalMessage(candidate));
|
|
133
|
+
}
|
|
134
|
+
}));
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
close,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function createFirestoreLikeSignalingAdapter({
|
|
142
|
+
addSignal,
|
|
143
|
+
listenSignals,
|
|
144
|
+
close = null,
|
|
145
|
+
} = {}) {
|
|
146
|
+
if (typeof addSignal !== 'function') {
|
|
147
|
+
throw new TypeError('addSignal must be a function');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (typeof listenSignals !== 'function') {
|
|
151
|
+
throw new TypeError('listenSignals must be a function');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return createCallbackSignalingAdapter({
|
|
155
|
+
publish: addSignal,
|
|
156
|
+
subscribe: listenSignals,
|
|
157
|
+
close,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function createPollingSignalingAdapter({
|
|
162
|
+
config,
|
|
163
|
+
publishSignal,
|
|
164
|
+
listSignals,
|
|
165
|
+
pollIntervalMs = requireResolvedConfig(config).webrtc.signaling.pollIntervalMs,
|
|
166
|
+
peerId = null,
|
|
167
|
+
after = 0,
|
|
168
|
+
pollTimeoutMs = requireResolvedConfig(config).webrtc.signaling.pollTimeoutMs,
|
|
169
|
+
failureThreshold = requireResolvedConfig(config).webrtc.signaling.failureThreshold,
|
|
170
|
+
pollBackoffBaseMs = requireResolvedConfig(config).webrtc.signaling.backoffBaseMs,
|
|
171
|
+
pollBackoffMaxMs = requireResolvedConfig(config).webrtc.signaling.backoffMaxMs,
|
|
172
|
+
onStatus = null,
|
|
173
|
+
close = null,
|
|
174
|
+
} = {}) {
|
|
175
|
+
if (typeof publishSignal !== 'function') {
|
|
176
|
+
throw new TypeError('publishSignal must be a function');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (typeof listSignals !== 'function') {
|
|
180
|
+
throw new TypeError('listSignals must be a function');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let cursor = {
|
|
184
|
+
sequence: null,
|
|
185
|
+
createdAt: Number(after || 0),
|
|
186
|
+
messageId: ''
|
|
187
|
+
};
|
|
188
|
+
let timer = null;
|
|
189
|
+
let stopped = false;
|
|
190
|
+
let circuitState = 'closed';
|
|
191
|
+
let consecutivePollFailures = 0;
|
|
192
|
+
let lastPollRetryAfterMs = 0;
|
|
193
|
+
const normalizedPollIntervalMs = Math.max(1, Number(pollIntervalMs || 1));
|
|
194
|
+
const emitStatus = (type, detail = {}) => {
|
|
195
|
+
if (typeof onStatus !== 'function') return;
|
|
196
|
+
try {
|
|
197
|
+
onStatus({ type, circuitState, consecutivePollFailures, ...detail });
|
|
198
|
+
} catch {
|
|
199
|
+
// Status presentation cannot break signaling recovery.
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
const nextPollDelay = (retryAfterMs = 0) => {
|
|
203
|
+
if (consecutivePollFailures === 0) return Math.max(normalizedPollIntervalMs, Number(retryAfterMs || 0));
|
|
204
|
+
return boundedRetryDelay({
|
|
205
|
+
consecutiveFailures: consecutivePollFailures,
|
|
206
|
+
baseDelayMs: pollBackoffBaseMs,
|
|
207
|
+
maxDelayMs: pollBackoffMaxMs,
|
|
208
|
+
retryAfterMs
|
|
209
|
+
});
|
|
210
|
+
};
|
|
211
|
+
const schedulePoll = (poll, delay = nextPollDelay()) => {
|
|
212
|
+
if (stopped || timer) return;
|
|
213
|
+
timer = globalThis.setTimeout(() => {
|
|
214
|
+
timer = null;
|
|
215
|
+
void poll();
|
|
216
|
+
}, Math.max(0, Number(delay || 0)));
|
|
217
|
+
};
|
|
218
|
+
const withTimeout = (promise) => new Promise((resolve, reject) => {
|
|
219
|
+
const timeout = globalThis.setTimeout(() => reject(new Error('signaling relay poll timed out')), Math.max(1, Number(pollTimeoutMs || 1)));
|
|
220
|
+
Promise.resolve(promise).then(
|
|
221
|
+
(value) => {
|
|
222
|
+
globalThis.clearTimeout(timeout);
|
|
223
|
+
resolve(value);
|
|
224
|
+
},
|
|
225
|
+
(error) => {
|
|
226
|
+
globalThis.clearTimeout(timeout);
|
|
227
|
+
reject(error);
|
|
228
|
+
}
|
|
229
|
+
);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const adapter = createCallbackSignalingAdapter({
|
|
233
|
+
publish: publishSignal,
|
|
234
|
+
subscribe(onMessage) {
|
|
235
|
+
const poll = async () => {
|
|
236
|
+
if (stopped) return;
|
|
237
|
+
let scheduledPollDelay = nextPollDelay();
|
|
238
|
+
if (circuitState === 'open') {
|
|
239
|
+
circuitState = 'half_open';
|
|
240
|
+
emitStatus('signaling-circuit-half-open');
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
const result = await withTimeout(listSignals({
|
|
244
|
+
after: cursor.createdAt,
|
|
245
|
+
afterId: cursor.messageId,
|
|
246
|
+
afterSequence: cursor.sequence,
|
|
247
|
+
peerId
|
|
248
|
+
}));
|
|
249
|
+
const messages = Array.isArray(result?.messages) ? result.messages : Array.isArray(result) ? result : [];
|
|
250
|
+
for (const message of messages) {
|
|
251
|
+
const normalized = normalizeSignalMessage(message);
|
|
252
|
+
onMessage(normalized);
|
|
253
|
+
}
|
|
254
|
+
const nextCursor = result?.nextCursor;
|
|
255
|
+
const hasSequence = Number.isSafeInteger(nextCursor?.sequence) && nextCursor.sequence >= 0;
|
|
256
|
+
if (nextCursor && (hasSequence || Number.isFinite(Number(nextCursor.createdAt)))) {
|
|
257
|
+
cursor = {
|
|
258
|
+
sequence: hasSequence ? nextCursor.sequence : null,
|
|
259
|
+
createdAt: Number(nextCursor.createdAt),
|
|
260
|
+
messageId: String(nextCursor.messageId || '')
|
|
261
|
+
};
|
|
262
|
+
} else if (messages.length > 0) {
|
|
263
|
+
const last = normalizeSignalMessage(messages.at(-1));
|
|
264
|
+
cursor = {
|
|
265
|
+
sequence: Number.isSafeInteger(last.relaySequence) ? last.relaySequence : null,
|
|
266
|
+
createdAt: Number(last.createdAt || 0),
|
|
267
|
+
messageId: String(last.id || '')
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (consecutivePollFailures > 0) emitStatus('signaling-poll-recovered');
|
|
271
|
+
if (circuitState !== 'closed') {
|
|
272
|
+
circuitState = 'closed';
|
|
273
|
+
emitStatus('signaling-circuit-closed');
|
|
274
|
+
}
|
|
275
|
+
consecutivePollFailures = 0;
|
|
276
|
+
lastPollRetryAfterMs = 0;
|
|
277
|
+
} catch (error) {
|
|
278
|
+
consecutivePollFailures += 1;
|
|
279
|
+
const threshold = Math.max(1, Number(failureThreshold || 1));
|
|
280
|
+
lastPollRetryAfterMs = retryAfterMsFromError(error, { maxDelayMs: pollBackoffMaxMs });
|
|
281
|
+
const retryDelayMs = nextPollDelay(lastPollRetryAfterMs);
|
|
282
|
+
scheduledPollDelay = retryDelayMs;
|
|
283
|
+
emitStatus('signaling-poll-failed', {
|
|
284
|
+
error: String(error?.message || error),
|
|
285
|
+
retryDelayMs,
|
|
286
|
+
retryAfterMs: lastPollRetryAfterMs || null
|
|
287
|
+
});
|
|
288
|
+
if (consecutivePollFailures >= threshold) {
|
|
289
|
+
circuitState = 'open';
|
|
290
|
+
emitStatus('signaling-circuit-open', { retryDelayMs });
|
|
291
|
+
} else {
|
|
292
|
+
circuitState = 'retrying';
|
|
293
|
+
}
|
|
294
|
+
} finally {
|
|
295
|
+
schedulePoll(poll, scheduledPollDelay);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
void poll();
|
|
299
|
+
return () => {
|
|
300
|
+
stopped = true;
|
|
301
|
+
if (timer) globalThis.clearTimeout(timer);
|
|
302
|
+
timer = null;
|
|
303
|
+
};
|
|
304
|
+
},
|
|
305
|
+
close() {
|
|
306
|
+
stopped = true;
|
|
307
|
+
if (timer) globalThis.clearTimeout(timer);
|
|
308
|
+
timer = null;
|
|
309
|
+
if (typeof close === 'function') close();
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
return Object.freeze({
|
|
313
|
+
...adapter,
|
|
314
|
+
getStatus() {
|
|
315
|
+
return Object.freeze({
|
|
316
|
+
cursor: { ...cursor },
|
|
317
|
+
circuitState,
|
|
318
|
+
consecutivePollFailures,
|
|
319
|
+
lastPollRetryAfterMs
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export function createPoolSdkSignalingAdapter({
|
|
326
|
+
config,
|
|
327
|
+
sdk,
|
|
328
|
+
sessionId,
|
|
329
|
+
peerId,
|
|
330
|
+
pollIntervalMs = requireResolvedConfig(config).webrtc.signaling.pollIntervalMs,
|
|
331
|
+
after = 0,
|
|
332
|
+
pollTimeoutMs = DEFAULT_SIGNAL_POLL_TIMEOUT_MS,
|
|
333
|
+
failureThreshold = DEFAULT_SIGNAL_FAILURE_THRESHOLD,
|
|
334
|
+
pollBackoffBaseMs = DEFAULT_SIGNAL_POLL_BACKOFF_BASE_MS,
|
|
335
|
+
pollBackoffMaxMs = DEFAULT_SIGNAL_POLL_BACKOFF_MAX_MS,
|
|
336
|
+
onStatus = null,
|
|
337
|
+
} = {}) {
|
|
338
|
+
if (!sdk || typeof sdk.publishSignal !== 'function' || typeof sdk.listSignals !== 'function') {
|
|
339
|
+
throw new TypeError('sdk must provide publishSignal() and listSignals()');
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const boundSessionId = requireString(sessionId, 'sessionId');
|
|
343
|
+
const boundPeerId = optionalString(peerId, 'peerId');
|
|
344
|
+
return createPollingSignalingAdapter({
|
|
345
|
+
config,
|
|
346
|
+
pollIntervalMs,
|
|
347
|
+
peerId: boundPeerId,
|
|
348
|
+
after,
|
|
349
|
+
pollTimeoutMs,
|
|
350
|
+
failureThreshold,
|
|
351
|
+
pollBackoffBaseMs,
|
|
352
|
+
pollBackoffMaxMs,
|
|
353
|
+
onStatus,
|
|
354
|
+
publishSignal(message) {
|
|
355
|
+
return sdk.publishSignal(boundSessionId, message).then((result) => result?.message || result);
|
|
356
|
+
},
|
|
357
|
+
listSignals(options = {}) {
|
|
358
|
+
return sdk.listSignals(boundSessionId, {
|
|
359
|
+
after: options.after,
|
|
360
|
+
afterId: options.afterId,
|
|
361
|
+
afterSequence: options.afterSequence,
|
|
362
|
+
peerId: options.peerId ?? boundPeerId
|
|
363
|
+
});
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export function createSignalingChannel({
|
|
369
|
+
sessionId,
|
|
370
|
+
assignmentId = null,
|
|
371
|
+
localPeerId,
|
|
372
|
+
remotePeerId = null,
|
|
373
|
+
adapter,
|
|
374
|
+
signalTtlMs = null,
|
|
375
|
+
now = () => Date.now(),
|
|
376
|
+
} = {}) {
|
|
377
|
+
if (!adapter || typeof adapter.publish !== 'function' || typeof adapter.subscribe !== 'function') {
|
|
378
|
+
throw new TypeError('adapter must provide publish() and subscribe()');
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const channelSessionId = requireString(sessionId, 'sessionId');
|
|
382
|
+
const channelLocalPeerId = requireString(localPeerId, 'localPeerId');
|
|
383
|
+
const channelRemotePeerId = optionalString(remotePeerId, 'remotePeerId');
|
|
384
|
+
const channelAssignmentId = optionalString(assignmentId, 'assignmentId');
|
|
385
|
+
let unsubscribe = null;
|
|
386
|
+
let closed = false;
|
|
387
|
+
|
|
388
|
+
function assertOpen() {
|
|
389
|
+
if (closed) {
|
|
390
|
+
throw new Error('signaling channel is closed');
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async function send(type, payload = null, options = {}) {
|
|
395
|
+
assertOpen();
|
|
396
|
+
|
|
397
|
+
const createdAt = now();
|
|
398
|
+
const ttl = options.signalTtlMs ?? signalTtlMs;
|
|
399
|
+
const message = createSignalMessage({
|
|
400
|
+
sessionId: channelSessionId,
|
|
401
|
+
assignmentId: options.assignmentId ?? channelAssignmentId,
|
|
402
|
+
type,
|
|
403
|
+
fromPeerId: channelLocalPeerId,
|
|
404
|
+
toPeerId: options.toPeerId ?? channelRemotePeerId,
|
|
405
|
+
payload,
|
|
406
|
+
createdAt,
|
|
407
|
+
expiresAt: ttl === null ? null : createdAt + ttl,
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
await adapter.publish(message);
|
|
411
|
+
return message;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function subscribe(onMessage, options = {}) {
|
|
415
|
+
assertOpen();
|
|
416
|
+
|
|
417
|
+
if (unsubscribe) {
|
|
418
|
+
throw new Error('signaling channel already has a subscriber');
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (typeof onMessage !== 'function') {
|
|
422
|
+
throw new TypeError('onMessage must be a function');
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
unsubscribe = adapter.subscribe((message) => {
|
|
426
|
+
if (!isSignalForPeer(message, {
|
|
427
|
+
sessionId: channelSessionId,
|
|
428
|
+
localPeerId: channelLocalPeerId,
|
|
429
|
+
remotePeerId: options.remotePeerId ?? channelRemotePeerId,
|
|
430
|
+
includeOwnSignals: Boolean(options.includeOwnSignals),
|
|
431
|
+
now: now(),
|
|
432
|
+
})) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
onMessage(message);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
return () => {
|
|
440
|
+
if (!unsubscribe) {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
unsubscribe();
|
|
445
|
+
unsubscribe = null;
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function close() {
|
|
450
|
+
if (closed) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
closed = true;
|
|
455
|
+
|
|
456
|
+
if (unsubscribe) {
|
|
457
|
+
unsubscribe();
|
|
458
|
+
unsubscribe = null;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (typeof adapter.close === 'function') {
|
|
462
|
+
adapter.close();
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return Object.freeze({
|
|
467
|
+
sessionId: channelSessionId,
|
|
468
|
+
assignmentId: channelAssignmentId,
|
|
469
|
+
localPeerId: channelLocalPeerId,
|
|
470
|
+
remotePeerId: channelRemotePeerId,
|
|
471
|
+
send,
|
|
472
|
+
sendOffer: (description, options) => send(SIGNAL_TYPES.OFFER, description, options),
|
|
473
|
+
sendAnswer: (description, options) => send(SIGNAL_TYPES.ANSWER, description, options),
|
|
474
|
+
sendIceCandidate: (candidate, options) => send(SIGNAL_TYPES.ICE_CANDIDATE, candidate, options),
|
|
475
|
+
sendClose: (reason = null, options) => send(SIGNAL_TYPES.CLOSE, { reason }, options),
|
|
476
|
+
sendPing: (payload = null, options) => send(SIGNAL_TYPES.PING, payload, options),
|
|
477
|
+
subscribe,
|
|
478
|
+
close,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function extractSignalRecords(record) {
|
|
483
|
+
if (Array.isArray(record)) {
|
|
484
|
+
return record;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (Array.isArray(record?.signals)) {
|
|
488
|
+
return record.signals;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (Array.isArray(record?.docs)) {
|
|
492
|
+
return record.docs.map((doc) => unwrapSignalRecord(doc));
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (Array.isArray(record?.docChanges?.())) {
|
|
496
|
+
return record.docChanges().map((change) => unwrapSignalRecord(change.doc));
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return [record];
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function unwrapSignalRecord(record) {
|
|
503
|
+
if (typeof record?.data === 'function') {
|
|
504
|
+
return record.data();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (record?.message) {
|
|
508
|
+
return record.message;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return record;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function normalizeUnsubscribe(value) {
|
|
515
|
+
if (typeof value === 'function') {
|
|
516
|
+
return value;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (value && typeof value.unsubscribe === 'function') {
|
|
520
|
+
return () => value.unsubscribe();
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return () => {};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function requireSignalType(value) {
|
|
527
|
+
const type = requireString(value, 'signal type');
|
|
528
|
+
const allowed = Object.values(SIGNAL_TYPES);
|
|
529
|
+
|
|
530
|
+
if (!allowed.includes(type)) {
|
|
531
|
+
throw new TypeError(`unsupported signal type: ${type}`);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return type;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function requireString(value, label) {
|
|
538
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
539
|
+
throw new TypeError(`${label} must be a non-empty string`);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return value;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function optionalString(value, label) {
|
|
546
|
+
if (value === null || value === undefined) {
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return requireString(value, label);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function requireFiniteNumber(value, label) {
|
|
554
|
+
if (!Number.isFinite(value)) {
|
|
555
|
+
throw new TypeError(`${label} must be a finite number`);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
return value;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function optionalFiniteNumber(value, label) {
|
|
562
|
+
if (value === null || value === undefined) {
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return requireFiniteNumber(value, label);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function isPlainObject(value) {
|
|
570
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
571
|
+
}
|