@cello-protocol/client 0.0.16 → 0.0.18
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/dist/client.d.ts +6 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +62 -22
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +22 -0
- package/dist/mcp-server.js.map +1 -1
- package/dist/types.d.ts +3 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/client.d.ts
CHANGED
|
@@ -113,6 +113,12 @@ import type { Stream } from "@libp2p/interface";
|
|
|
113
113
|
import type { CelloClient, SendResult, SessionRecord } from "./types.js";
|
|
114
114
|
import type { Logger } from "@cello-protocol/interfaces";
|
|
115
115
|
import type { ClientStatePersistence } from "./client-state-persistence.js";
|
|
116
|
+
/**
|
|
117
|
+
* Map a raw decoded session_request_error frame to an InitiateSessionResult.
|
|
118
|
+
* Exported for direct unit testing (AC-005, AC-006).
|
|
119
|
+
* The frame must have type === "session_request_error".
|
|
120
|
+
*/
|
|
121
|
+
export declare function mapSessionRequestErrorFrame(frame: Record<string, unknown>): import("./types.js").InitiateSessionResult;
|
|
116
122
|
export declare function createClient(node: CelloNode, keyProvider: KeyProvider, opts?: {
|
|
117
123
|
onMessageQueued?: (senderPubkeyHex: string) => void;
|
|
118
124
|
contentGraceMs?: number;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2GG;AAaH,OAAO,KAAK,EAAa,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,EACV,WAAW,EAA+B,UAAU,EAAE,aAAa,EAGpE,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -1176,7 +1176,7 @@ class CelloClientImpl {
|
|
|
1176
1176
|
return { ok: false, reason: "session_not_found" };
|
|
1177
1177
|
if (session.desynchronized)
|
|
1178
1178
|
return { ok: false, reason: "session_desynchronized" };
|
|
1179
|
-
if (session.status === "sealing" || session.status === "sealed" || session.status === "seal_deferred")
|
|
1179
|
+
if (session.status === "sealing" || session.status === "sealed" || session.status === "seal_deferred" || session.status === "seal_rejected")
|
|
1180
1180
|
return { ok: false, reason: "session_sealed" };
|
|
1181
1181
|
// SESSION-006 AC-001/AC-003: transport_lost means relay stream is gone or being reconnected
|
|
1182
1182
|
if (session.status === "transport_lost")
|
|
@@ -1507,9 +1507,9 @@ class CelloClientImpl {
|
|
|
1507
1507
|
// mutated session status while this call was awaiting the stream open.
|
|
1508
1508
|
// TypeScript narrows session.status to "active" at this point (line 1732 guard), but
|
|
1509
1509
|
// async suspension means the actual value may have changed — re-read via a typed cast.
|
|
1510
|
-
// Mirror the guard at #sendMessageLocked: sealing/sealed/seal_deferred → "session_sealed".
|
|
1510
|
+
// Mirror the guard at #sendMessageLocked: sealing/sealed/seal_deferred/seal_rejected → "session_sealed".
|
|
1511
1511
|
const statusNow = session.status;
|
|
1512
|
-
if (statusNow === "sealing" || statusNow === "sealed" || statusNow === "seal_deferred") {
|
|
1512
|
+
if (statusNow === "sealing" || statusNow === "sealed" || statusNow === "seal_deferred" || statusNow === "seal_rejected") {
|
|
1513
1513
|
return { ok: false, reason: "session_sealed" };
|
|
1514
1514
|
}
|
|
1515
1515
|
if (statusNow !== "active")
|
|
@@ -2978,13 +2978,40 @@ class CelloClientImpl {
|
|
|
2978
2978
|
// PERSIST-024 FINDING-2: persist the responder's sealing status so a crash here
|
|
2979
2979
|
// does not reload the session as "active". Must happen after status mutation.
|
|
2980
2980
|
void this.#persistence?.persistSession(sessionIdHex, session);
|
|
2981
|
-
void
|
|
2981
|
+
void (async () => {
|
|
2982
|
+
// Mirror the initiator path: ensure the signaling stream is alive before
|
|
2983
|
+
// submitting the SEAL leaf. The directory replies (seal_verified) on this
|
|
2984
|
+
// stream. If dead, reconnect first — else seal_verified is never received,
|
|
2985
|
+
// the 15-second FROST timeout fires, and the session ends as seal_deferred.
|
|
2986
|
+
if (!this.#persistentSignalingStream || this.#persistentSignalingStream.status !== "open") {
|
|
2987
|
+
this.#logger.info("seal.reconnect.attempted", { sessionId: sessionIdHex, correlationId: sessionIdHex, role: "responder" });
|
|
2988
|
+
this.#persistentSignalingStream = null;
|
|
2989
|
+
this.#persistentSignalingIter = null;
|
|
2990
|
+
const opened = await this.#openPersistentSignalingStream();
|
|
2991
|
+
if (!opened || !this.#persistentSignalingStream) {
|
|
2992
|
+
const s = this.#sessions.get(sessionIdHex);
|
|
2993
|
+
if (s && s.status === "sealing") {
|
|
2994
|
+
s.status = "seal_deferred";
|
|
2995
|
+
void this.#persistence?.persistSession(sessionIdHex, s);
|
|
2996
|
+
}
|
|
2997
|
+
return;
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
// Re-fetch session after any async suspend — state may have been mutated by concurrent
|
|
3001
|
+
// callers (desync, close, reconciliation). Using the stale pre-reconnect reference could
|
|
3002
|
+
// produce the wrong Merkle root in the TBS and cause seal_rejected_tree_mismatch.
|
|
3003
|
+
const freshSession = this.#sessions.get(sessionIdHex);
|
|
3004
|
+
if (!freshSession || freshSession.status !== "sealing")
|
|
3005
|
+
return;
|
|
3006
|
+
const result = await this.#submitSealLeaf(sessionIdHex, freshSession, "responder");
|
|
2982
3007
|
if (!result.ok) {
|
|
2983
3008
|
const s = this.#sessions.get(sessionIdHex);
|
|
2984
|
-
if (s && s.status === "sealing")
|
|
3009
|
+
if (s && s.status === "sealing") {
|
|
2985
3010
|
s.status = "seal_rejected";
|
|
3011
|
+
void this.#persistence?.persistSession(sessionIdHex, s);
|
|
3012
|
+
}
|
|
2986
3013
|
}
|
|
2987
|
-
});
|
|
3014
|
+
})();
|
|
2988
3015
|
}
|
|
2989
3016
|
else {
|
|
2990
3017
|
// Counterparty message: enqueue for receiveMessage callers.
|
|
@@ -4794,22 +4821,7 @@ class CelloClientImpl {
|
|
|
4794
4821
|
}
|
|
4795
4822
|
const frame = responseFrame;
|
|
4796
4823
|
if (frame["type"] === "session_request_error") {
|
|
4797
|
-
|
|
4798
|
-
if (reason === "target_offline")
|
|
4799
|
-
return { ok: false, reason: "target_offline" };
|
|
4800
|
-
if (reason === "relay_unavailable")
|
|
4801
|
-
return { ok: false, reason: "relay_unavailable" };
|
|
4802
|
-
if (reason === "frost_signer_not_configured")
|
|
4803
|
-
return { ok: false, reason: "frost_signer_not_configured" };
|
|
4804
|
-
if (reason === "directory_below_threshold")
|
|
4805
|
-
return { ok: false, reason: "directory_below_threshold" };
|
|
4806
|
-
if (reason === "ceremony_conflict")
|
|
4807
|
-
return { ok: false, reason: "ceremony_conflict" };
|
|
4808
|
-
if (reason === "no_connection")
|
|
4809
|
-
return { ok: false, reason: "no_connection" };
|
|
4810
|
-
if (reason === "connection_id_required")
|
|
4811
|
-
return { ok: false, reason: "no_connection" };
|
|
4812
|
-
return { ok: false, reason: "directory_unreachable" };
|
|
4824
|
+
return mapSessionRequestErrorFrame(frame);
|
|
4813
4825
|
}
|
|
4814
4826
|
if (frame["type"] === "session_assignment") {
|
|
4815
4827
|
const rawAssignment = frame["assignment"];
|
|
@@ -5406,6 +5418,34 @@ function parseStringArray(v) {
|
|
|
5406
5418
|
return null;
|
|
5407
5419
|
return v;
|
|
5408
5420
|
}
|
|
5421
|
+
// ─── Session request error mapping ───────────────────────────────────────────
|
|
5422
|
+
/**
|
|
5423
|
+
* Map a raw decoded session_request_error frame to an InitiateSessionResult.
|
|
5424
|
+
* Exported for direct unit testing (AC-005, AC-006).
|
|
5425
|
+
* The frame must have type === "session_request_error".
|
|
5426
|
+
*/
|
|
5427
|
+
export function mapSessionRequestErrorFrame(frame) {
|
|
5428
|
+
const reason = frame["reason"];
|
|
5429
|
+
if (reason === "target_offline")
|
|
5430
|
+
return { ok: false, reason: "target_offline" };
|
|
5431
|
+
if (reason === "relay_unavailable")
|
|
5432
|
+
return { ok: false, reason: "relay_unavailable" };
|
|
5433
|
+
if (reason === "frost_signer_not_configured")
|
|
5434
|
+
return { ok: false, reason: "frost_signer_not_configured" };
|
|
5435
|
+
if (reason === "directory_below_threshold")
|
|
5436
|
+
return { ok: false, reason: "directory_below_threshold" };
|
|
5437
|
+
if (reason === "ceremony_timeout")
|
|
5438
|
+
return { ok: false, reason: "ceremony_timeout" };
|
|
5439
|
+
if (reason === "ceremony_exhausted")
|
|
5440
|
+
return { ok: false, reason: "ceremony_exhausted" };
|
|
5441
|
+
if (reason === "ceremony_conflict")
|
|
5442
|
+
return { ok: false, reason: "ceremony_conflict" };
|
|
5443
|
+
if (reason === "no_connection")
|
|
5444
|
+
return { ok: false, reason: "no_connection" };
|
|
5445
|
+
if (reason === "connection_id_required")
|
|
5446
|
+
return { ok: false, reason: "no_connection" };
|
|
5447
|
+
return { ok: false, reason: "directory_unreachable" };
|
|
5448
|
+
}
|
|
5409
5449
|
// ─── Factory ─────────────────────────────────────────────────────────────────
|
|
5410
5450
|
export function createClient(node, keyProvider, opts) {
|
|
5411
5451
|
return new CelloClientImpl(node, keyProvider, opts?.onMessageQueued, opts?.contentGraceMs, opts?.reconnectTimeoutMs, opts?.thresholdSigner, opts?.sealFrostTimeoutMs, opts?.directoryEndpoint ?? null, opts?.mlDsaKeyFile, opts?.connectionPolicy, opts?.connectionTimeoutMs, opts?.round2TimeoutMs, opts?.trackEvaluateCount, opts?.whitelist, opts?.onConnectionPendingReview, opts?.crossCheckDirectoryOnInbound, opts?.logger, opts?.persistence);
|