@cello-protocol/daemon 0.0.3 → 0.0.5
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/agent-loader.d.ts +41 -0
- package/dist/agent-loader.d.ts.map +1 -0
- package/dist/agent-loader.js +94 -0
- package/dist/agent-loader.js.map +1 -0
- package/dist/bin/cello-daemon.d.ts +13 -0
- package/dist/bin/cello-daemon.d.ts.map +1 -0
- package/dist/bin/cello-daemon.js +170 -0
- package/dist/bin/cello-daemon.js.map +1 -0
- package/dist/cello-node-transport-dialer.d.ts +59 -0
- package/dist/cello-node-transport-dialer.d.ts.map +1 -0
- package/dist/cello-node-transport-dialer.js +108 -0
- package/dist/cello-node-transport-dialer.js.map +1 -0
- package/dist/challenge-verifier.d.ts +12 -0
- package/dist/challenge-verifier.d.ts.map +1 -0
- package/dist/challenge-verifier.js +11 -0
- package/dist/challenge-verifier.js.map +1 -0
- package/dist/connect-or-start.d.ts +25 -0
- package/dist/connect-or-start.d.ts.map +1 -0
- package/dist/connect-or-start.js +117 -0
- package/dist/connect-or-start.js.map +1 -0
- package/dist/content-park-client.d.ts +49 -0
- package/dist/content-park-client.d.ts.map +1 -0
- package/dist/content-park-client.js +196 -0
- package/dist/content-park-client.js.map +1 -0
- package/dist/daemon.d.ts +65 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +3202 -0
- package/dist/daemon.js.map +1 -0
- package/dist/directory-bootstrap.d.ts +55 -0
- package/dist/directory-bootstrap.d.ts.map +1 -0
- package/dist/directory-bootstrap.js +102 -0
- package/dist/directory-bootstrap.js.map +1 -0
- package/dist/file-manifest-provider.d.ts +18 -0
- package/dist/file-manifest-provider.d.ts.map +1 -0
- package/dist/file-manifest-provider.js +72 -0
- package/dist/file-manifest-provider.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/ipc-client.d.ts +31 -0
- package/dist/ipc-client.d.ts.map +1 -0
- package/dist/ipc-client.js +112 -0
- package/dist/ipc-client.js.map +1 -0
- package/dist/ipc-server.d.ts +49 -0
- package/dist/ipc-server.d.ts.map +1 -0
- package/dist/ipc-server.js +268 -0
- package/dist/ipc-server.js.map +1 -0
- package/dist/lock-file.d.ts +27 -0
- package/dist/lock-file.d.ts.map +1 -0
- package/dist/lock-file.js +84 -0
- package/dist/lock-file.js.map +1 -0
- package/dist/manifest-loader.d.ts +33 -0
- package/dist/manifest-loader.d.ts.map +1 -0
- package/dist/manifest-loader.js +70 -0
- package/dist/manifest-loader.js.map +1 -0
- package/dist/manifest-poll-scheduler.d.ts +31 -0
- package/dist/manifest-poll-scheduler.d.ts.map +1 -0
- package/dist/manifest-poll-scheduler.js +59 -0
- package/dist/manifest-poll-scheduler.js.map +1 -0
- package/dist/manifest-version-store-file.d.ts +18 -0
- package/dist/manifest-version-store-file.d.ts.map +1 -0
- package/dist/manifest-version-store-file.js +40 -0
- package/dist/manifest-version-store-file.js.map +1 -0
- package/dist/manifest-version-store.d.ts +14 -0
- package/dist/manifest-version-store.d.ts.map +1 -0
- package/dist/manifest-version-store.js +13 -0
- package/dist/manifest-version-store.js.map +1 -0
- package/dist/network-directory-node.d.ts +94 -0
- package/dist/network-directory-node.d.ts.map +1 -0
- package/dist/network-directory-node.js +626 -0
- package/dist/network-directory-node.js.map +1 -0
- package/dist/nonce-dedup.d.ts +68 -0
- package/dist/nonce-dedup.d.ts.map +1 -0
- package/dist/nonce-dedup.js +204 -0
- package/dist/nonce-dedup.js.map +1 -0
- package/dist/notification-dispatcher.d.ts +65 -0
- package/dist/notification-dispatcher.d.ts.map +1 -0
- package/dist/notification-dispatcher.js +138 -0
- package/dist/notification-dispatcher.js.map +1 -0
- package/dist/registration-context.d.ts +69 -0
- package/dist/registration-context.d.ts.map +1 -0
- package/dist/registration-context.js +118 -0
- package/dist/registration-context.js.map +1 -0
- package/dist/registration-manager.d.ts +72 -0
- package/dist/registration-manager.d.ts.map +1 -0
- package/dist/registration-manager.js +267 -0
- package/dist/registration-manager.js.map +1 -0
- package/dist/registration-persistence.d.ts +131 -0
- package/dist/registration-persistence.d.ts.map +1 -0
- package/dist/registration-persistence.js +233 -0
- package/dist/registration-persistence.js.map +1 -0
- package/dist/retry-queue.d.ts +144 -0
- package/dist/retry-queue.d.ts.map +1 -0
- package/dist/retry-queue.js +444 -0
- package/dist/retry-queue.js.map +1 -0
- package/dist/seal-frontier-verify.d.ts +58 -0
- package/dist/seal-frontier-verify.d.ts.map +1 -0
- package/dist/seal-frontier-verify.js +87 -0
- package/dist/seal-frontier-verify.js.map +1 -0
- package/dist/seal-legibility-tbs.d.ts +25 -0
- package/dist/seal-legibility-tbs.d.ts.map +1 -0
- package/dist/seal-legibility-tbs.js +78 -0
- package/dist/seal-legibility-tbs.js.map +1 -0
- package/dist/seal-upgrade.d.ts +90 -0
- package/dist/seal-upgrade.d.ts.map +1 -0
- package/dist/seal-upgrade.js +178 -0
- package/dist/seal-upgrade.js.map +1 -0
- package/dist/session-assignment-parser.d.ts +22 -0
- package/dist/session-assignment-parser.d.ts.map +1 -0
- package/dist/session-assignment-parser.js +139 -0
- package/dist/session-assignment-parser.js.map +1 -0
- package/dist/session-ceremony.d.ts +156 -0
- package/dist/session-ceremony.d.ts.map +1 -0
- package/dist/session-ceremony.js +447 -0
- package/dist/session-ceremony.js.map +1 -0
- package/dist/session-connection-gater.d.ts +91 -0
- package/dist/session-connection-gater.d.ts.map +1 -0
- package/dist/session-connection-gater.js +146 -0
- package/dist/session-connection-gater.js.map +1 -0
- package/dist/session-node-manager.d.ts +585 -0
- package/dist/session-node-manager.d.ts.map +1 -0
- package/dist/session-node-manager.js +2609 -0
- package/dist/session-node-manager.js.map +1 -0
- package/dist/session-relay-client.d.ts +101 -0
- package/dist/session-relay-client.d.ts.map +1 -0
- package/dist/session-relay-client.js +520 -0
- package/dist/session-relay-client.js.map +1 -0
- package/dist/session-tree.d.ts +80 -0
- package/dist/session-tree.d.ts.map +1 -0
- package/dist/session-tree.js +123 -0
- package/dist/session-tree.js.map +1 -0
- package/dist/signaling-connect.d.ts +83 -0
- package/dist/signaling-connect.d.ts.map +1 -0
- package/dist/signaling-connect.js +266 -0
- package/dist/signaling-connect.js.map +1 -0
- package/dist/transcript-cipher.d.ts +31 -0
- package/dist/transcript-cipher.d.ts.map +1 -0
- package/dist/transcript-cipher.js +74 -0
- package/dist/transcript-cipher.js.map +1 -0
- package/dist/transport-composition.d.ts +31 -0
- package/dist/transport-composition.d.ts.map +1 -0
- package/dist/transport-composition.js +55 -0
- package/dist/transport-composition.js.map +1 -0
- package/dist/transport-selector.d.ts +189 -0
- package/dist/transport-selector.d.ts.map +1 -0
- package/dist/transport-selector.js +195 -0
- package/dist/transport-selector.js.map +1 -0
- package/dist/types.d.ts +265 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +33 -0
- package/dist/types.js.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO Daemon — NonceDedupStore
|
|
3
|
+
*
|
|
4
|
+
* Per-session LRU set of already-seen incoming nonces. When an incoming message
|
|
5
|
+
* arrives, the daemon checks its nonce here BEFORE delivering to cello_receive.
|
|
6
|
+
* Duplicates are silently discarded.
|
|
7
|
+
*
|
|
8
|
+
* Persistence: SQLCipher table `session_seen_nonces`.
|
|
9
|
+
* Cap: 10,000 nonces per session (LRU eviction).
|
|
10
|
+
*
|
|
11
|
+
* Pseudocode (SPARC Phase P):
|
|
12
|
+
*
|
|
13
|
+
* 1. constructor(db, logger):
|
|
14
|
+
* - Store db handle and logger reference
|
|
15
|
+
* - Initialize empty per-session Map<sessionId, Map<nonceHex, seenAt>>
|
|
16
|
+
* - Create session_seen_nonces table IF NOT EXISTS
|
|
17
|
+
* - Create index on (session_id, seen_at ASC) for LRU eviction
|
|
18
|
+
*
|
|
19
|
+
* 2. loadFromDb():
|
|
20
|
+
* - SELECT all rows from session_seen_nonces ORDER BY session_id, seen_at ASC
|
|
21
|
+
* - Group into per-session Maps of nonceHex → seenAt
|
|
22
|
+
* - This populates the in-memory LRU for duplicate checks
|
|
23
|
+
*
|
|
24
|
+
* 3. has(sessionId, nonce: Uint8Array):
|
|
25
|
+
* - Convert nonce to hex: Buffer.from(nonce).toString('hex')
|
|
26
|
+
* - Return whether the session's set contains this nonceHex
|
|
27
|
+
*
|
|
28
|
+
* 4. add(sessionId, nonce: Uint8Array, senderPubkey: Uint8Array):
|
|
29
|
+
* - Convert to hex strings
|
|
30
|
+
* - If already present: log message.nonce.duplicate DEBUG, return true (is duplicate)
|
|
31
|
+
* - If session set size >= 10,000: LRU evict oldest (lowest seen_at)
|
|
32
|
+
* - DELETE from session_seen_nonces WHERE session_id AND nonce_hex = oldest
|
|
33
|
+
* - Remove from in-memory map
|
|
34
|
+
* - INSERT into session_seen_nonces
|
|
35
|
+
* - Add to in-memory map
|
|
36
|
+
* - Return false (not a duplicate)
|
|
37
|
+
*
|
|
38
|
+
* 5. checkAndAdd(sessionId, nonce: Uint8Array, senderPubkey: Uint8Array):
|
|
39
|
+
* - If has(sessionId, nonce): log duplicate, return true (duplicate)
|
|
40
|
+
* - Otherwise: add, return false (new)
|
|
41
|
+
* - Combined check-and-add for atomic dedup logic
|
|
42
|
+
*/
|
|
43
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
44
|
+
import type { Logger } from "./types.js";
|
|
45
|
+
/** LRU cap per outline.md Resource Caps. */
|
|
46
|
+
export declare const NONCE_DEDUP_CAP = 10000;
|
|
47
|
+
export declare class NonceDedupStore {
|
|
48
|
+
#private;
|
|
49
|
+
constructor(db: DatabaseSync, logger: Logger);
|
|
50
|
+
/**
|
|
51
|
+
* Load all nonce entries from SQLCipher into memory.
|
|
52
|
+
* Must complete BEFORE the IPC socket opens (AC-007).
|
|
53
|
+
*/
|
|
54
|
+
loadFromDb(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Check if a nonce has been seen for a session.
|
|
57
|
+
*/
|
|
58
|
+
has(sessionId: string, nonce: Uint8Array): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Check if nonce is a duplicate; if not, add it to the store.
|
|
61
|
+
* Returns true if the nonce is a DUPLICATE (should be discarded).
|
|
62
|
+
* Returns false if the nonce is NEW (should be delivered).
|
|
63
|
+
*
|
|
64
|
+
* This is the combined check-and-add for the daemon's inbound message path.
|
|
65
|
+
*/
|
|
66
|
+
checkAndAdd(sessionId: string, nonce: Uint8Array, senderPubkey: Uint8Array): boolean;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=nonce-dedup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nonce-dedup.d.ts","sourceRoot":"","sources":["../src/nonce-dedup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,4CAA4C;AAC5C,eAAO,MAAM,eAAe,QAAS,CAAC;AAQtC,qBAAa,eAAe;;gBAUd,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM;IAoB5C;;;OAGG;IACH,UAAU,IAAI,IAAI;IA0BlB;;OAEG;IACH,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO;IAOlD;;;;;;OAMG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,GAAG,OAAO;CAoGrF"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO Daemon — NonceDedupStore
|
|
3
|
+
*
|
|
4
|
+
* Per-session LRU set of already-seen incoming nonces. When an incoming message
|
|
5
|
+
* arrives, the daemon checks its nonce here BEFORE delivering to cello_receive.
|
|
6
|
+
* Duplicates are silently discarded.
|
|
7
|
+
*
|
|
8
|
+
* Persistence: SQLCipher table `session_seen_nonces`.
|
|
9
|
+
* Cap: 10,000 nonces per session (LRU eviction).
|
|
10
|
+
*
|
|
11
|
+
* Pseudocode (SPARC Phase P):
|
|
12
|
+
*
|
|
13
|
+
* 1. constructor(db, logger):
|
|
14
|
+
* - Store db handle and logger reference
|
|
15
|
+
* - Initialize empty per-session Map<sessionId, Map<nonceHex, seenAt>>
|
|
16
|
+
* - Create session_seen_nonces table IF NOT EXISTS
|
|
17
|
+
* - Create index on (session_id, seen_at ASC) for LRU eviction
|
|
18
|
+
*
|
|
19
|
+
* 2. loadFromDb():
|
|
20
|
+
* - SELECT all rows from session_seen_nonces ORDER BY session_id, seen_at ASC
|
|
21
|
+
* - Group into per-session Maps of nonceHex → seenAt
|
|
22
|
+
* - This populates the in-memory LRU for duplicate checks
|
|
23
|
+
*
|
|
24
|
+
* 3. has(sessionId, nonce: Uint8Array):
|
|
25
|
+
* - Convert nonce to hex: Buffer.from(nonce).toString('hex')
|
|
26
|
+
* - Return whether the session's set contains this nonceHex
|
|
27
|
+
*
|
|
28
|
+
* 4. add(sessionId, nonce: Uint8Array, senderPubkey: Uint8Array):
|
|
29
|
+
* - Convert to hex strings
|
|
30
|
+
* - If already present: log message.nonce.duplicate DEBUG, return true (is duplicate)
|
|
31
|
+
* - If session set size >= 10,000: LRU evict oldest (lowest seen_at)
|
|
32
|
+
* - DELETE from session_seen_nonces WHERE session_id AND nonce_hex = oldest
|
|
33
|
+
* - Remove from in-memory map
|
|
34
|
+
* - INSERT into session_seen_nonces
|
|
35
|
+
* - Add to in-memory map
|
|
36
|
+
* - Return false (not a duplicate)
|
|
37
|
+
*
|
|
38
|
+
* 5. checkAndAdd(sessionId, nonce: Uint8Array, senderPubkey: Uint8Array):
|
|
39
|
+
* - If has(sessionId, nonce): log duplicate, return true (duplicate)
|
|
40
|
+
* - Otherwise: add, return false (new)
|
|
41
|
+
* - Combined check-and-add for atomic dedup logic
|
|
42
|
+
*/
|
|
43
|
+
/** LRU cap per outline.md Resource Caps. */
|
|
44
|
+
export const NONCE_DEDUP_CAP = 10_000;
|
|
45
|
+
export class NonceDedupStore {
|
|
46
|
+
#db;
|
|
47
|
+
#logger;
|
|
48
|
+
/**
|
|
49
|
+
* Per-session ordered map: nonceHex → NonceEntry.
|
|
50
|
+
* Insertion order approximates LRU (oldest first in iteration).
|
|
51
|
+
* For exact LRU we track seenAt and find the minimum.
|
|
52
|
+
*/
|
|
53
|
+
#sessions = new Map();
|
|
54
|
+
constructor(db, logger) {
|
|
55
|
+
this.#db = db;
|
|
56
|
+
this.#logger = logger;
|
|
57
|
+
// Create table + index if not exists (inline migration — not Flyway)
|
|
58
|
+
this.#db.exec(`
|
|
59
|
+
CREATE TABLE IF NOT EXISTS session_seen_nonces (
|
|
60
|
+
session_id TEXT NOT NULL,
|
|
61
|
+
nonce_hex TEXT NOT NULL,
|
|
62
|
+
sender_pubkey TEXT NOT NULL,
|
|
63
|
+
seen_at INTEGER NOT NULL,
|
|
64
|
+
PRIMARY KEY (session_id, nonce_hex)
|
|
65
|
+
)
|
|
66
|
+
`);
|
|
67
|
+
this.#db.exec(`
|
|
68
|
+
CREATE INDEX IF NOT EXISTS seen_nonces_by_session_seen_at
|
|
69
|
+
ON session_seen_nonces(session_id, seen_at ASC)
|
|
70
|
+
`);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Load all nonce entries from SQLCipher into memory.
|
|
74
|
+
* Must complete BEFORE the IPC socket opens (AC-007).
|
|
75
|
+
*/
|
|
76
|
+
loadFromDb() {
|
|
77
|
+
const rows = this.#db
|
|
78
|
+
.prepare("SELECT session_id, nonce_hex, sender_pubkey, seen_at FROM session_seen_nonces ORDER BY session_id, seen_at ASC")
|
|
79
|
+
.all();
|
|
80
|
+
this.#sessions.clear();
|
|
81
|
+
for (const row of rows) {
|
|
82
|
+
let sessionMap = this.#sessions.get(row.session_id);
|
|
83
|
+
if (!sessionMap) {
|
|
84
|
+
sessionMap = new Map();
|
|
85
|
+
this.#sessions.set(row.session_id, sessionMap);
|
|
86
|
+
}
|
|
87
|
+
sessionMap.set(row.nonce_hex, {
|
|
88
|
+
nonceHex: row.nonce_hex,
|
|
89
|
+
senderPubkey: row.sender_pubkey,
|
|
90
|
+
seenAt: row.seen_at,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Check if a nonce has been seen for a session.
|
|
96
|
+
*/
|
|
97
|
+
has(sessionId, nonce) {
|
|
98
|
+
const nonceHex = Buffer.from(nonce).toString("hex");
|
|
99
|
+
const sessionMap = this.#sessions.get(sessionId);
|
|
100
|
+
if (!sessionMap)
|
|
101
|
+
return false;
|
|
102
|
+
return sessionMap.has(nonceHex);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Check if nonce is a duplicate; if not, add it to the store.
|
|
106
|
+
* Returns true if the nonce is a DUPLICATE (should be discarded).
|
|
107
|
+
* Returns false if the nonce is NEW (should be delivered).
|
|
108
|
+
*
|
|
109
|
+
* This is the combined check-and-add for the daemon's inbound message path.
|
|
110
|
+
*/
|
|
111
|
+
checkAndAdd(sessionId, nonce, senderPubkey) {
|
|
112
|
+
const nonceHex = Buffer.from(nonce).toString("hex");
|
|
113
|
+
const senderPubkeyHex = Buffer.from(senderPubkey).toString("hex");
|
|
114
|
+
let sessionMap = this.#sessions.get(sessionId);
|
|
115
|
+
if (!sessionMap) {
|
|
116
|
+
sessionMap = new Map();
|
|
117
|
+
this.#sessions.set(sessionId, sessionMap);
|
|
118
|
+
}
|
|
119
|
+
// Check for duplicate
|
|
120
|
+
if (sessionMap.has(nonceHex)) {
|
|
121
|
+
this.#logger.debug("message.nonce.duplicate", {
|
|
122
|
+
sessionId,
|
|
123
|
+
nonce: nonceHex,
|
|
124
|
+
senderPubkey: senderPubkeyHex,
|
|
125
|
+
});
|
|
126
|
+
return true; // duplicate — discard
|
|
127
|
+
}
|
|
128
|
+
// LRU eviction if at cap
|
|
129
|
+
if (sessionMap.size >= NONCE_DEDUP_CAP) {
|
|
130
|
+
this.#evictOldest(sessionId, sessionMap);
|
|
131
|
+
}
|
|
132
|
+
// Add new nonce
|
|
133
|
+
const seenAt = Date.now();
|
|
134
|
+
const entry = { nonceHex, senderPubkey: senderPubkeyHex, seenAt };
|
|
135
|
+
sessionMap.set(nonceHex, entry);
|
|
136
|
+
// Persist to SQLCipher
|
|
137
|
+
try {
|
|
138
|
+
this.#db
|
|
139
|
+
.prepare(`INSERT INTO session_seen_nonces (session_id, nonce_hex, sender_pubkey, seen_at)
|
|
140
|
+
VALUES (?, ?, ?, ?)`)
|
|
141
|
+
.run(sessionId, nonceHex, senderPubkeyHex, seenAt);
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
this.#logger.error("message.nonce.persist.failed", {
|
|
145
|
+
sessionId,
|
|
146
|
+
nonce: nonceHex,
|
|
147
|
+
error: err instanceof Error ? err.message : String(err),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return false; // new — deliver
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Evict the oldest entry (lowest seen_at) for a session.
|
|
154
|
+
* Uses the SQLite index on (session_id, seen_at ASC) for O(log n) lookup
|
|
155
|
+
* instead of O(n) in-memory scan.
|
|
156
|
+
*/
|
|
157
|
+
#evictOldest(sessionId, sessionMap) {
|
|
158
|
+
// Use indexed query to find the oldest — O(log n) via the seen_at index
|
|
159
|
+
let oldestHex = null;
|
|
160
|
+
try {
|
|
161
|
+
const row = this.#db
|
|
162
|
+
.prepare("SELECT nonce_hex FROM session_seen_nonces WHERE session_id = ? ORDER BY seen_at ASC LIMIT 1")
|
|
163
|
+
.get(sessionId);
|
|
164
|
+
if (row) {
|
|
165
|
+
oldestHex = row.nonce_hex;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
this.#logger.error("message.nonce.persist.failed", {
|
|
170
|
+
sessionId,
|
|
171
|
+
nonce: "eviction_query",
|
|
172
|
+
error: err instanceof Error ? err.message : String(err),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
// Fallback: if DB query failed, scan in-memory
|
|
176
|
+
if (!oldestHex) {
|
|
177
|
+
let oldest = null;
|
|
178
|
+
for (const entry of sessionMap.values()) {
|
|
179
|
+
if (!oldest || entry.seenAt < oldest.seenAt) {
|
|
180
|
+
oldest = entry;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (!oldest)
|
|
184
|
+
return;
|
|
185
|
+
oldestHex = oldest.nonceHex;
|
|
186
|
+
}
|
|
187
|
+
// Delete from DB
|
|
188
|
+
try {
|
|
189
|
+
this.#db
|
|
190
|
+
.prepare("DELETE FROM session_seen_nonces WHERE session_id = ? AND nonce_hex = ?")
|
|
191
|
+
.run(sessionId, oldestHex);
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
this.#logger.error("message.nonce.persist.failed", {
|
|
195
|
+
sessionId,
|
|
196
|
+
nonce: oldestHex,
|
|
197
|
+
error: err instanceof Error ? err.message : String(err),
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
// Remove from memory
|
|
201
|
+
sessionMap.delete(oldestHex);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=nonce-dedup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nonce-dedup.js","sourceRoot":"","sources":["../src/nonce-dedup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO Daemon — NotificationDispatcher
|
|
3
|
+
*
|
|
4
|
+
* Owns the routing table for MCP notifications. Applies per-notification-type
|
|
5
|
+
* routing rules:
|
|
6
|
+
* - agent_state_changed → ALL active IPC connections
|
|
7
|
+
* - agent_current_changed → triggering connection ONLY
|
|
8
|
+
* - session_state_changed → connections where affected agent is current
|
|
9
|
+
*
|
|
10
|
+
* Pseudocode (SPARC Phase P):
|
|
11
|
+
*
|
|
12
|
+
* registerConnection(connectionId, socket):
|
|
13
|
+
* 1. Add entry to routing table: { connectionId → { socket, currentAgent: null } }
|
|
14
|
+
*
|
|
15
|
+
* unregisterConnection(connectionId):
|
|
16
|
+
* 1. Remove entry from routing table
|
|
17
|
+
*
|
|
18
|
+
* setCurrentAgent(connectionId, agentName):
|
|
19
|
+
* 1. Update currentAgent for the connection entry
|
|
20
|
+
*
|
|
21
|
+
* dispatchAgentStateChanged(agentName, state, reason):
|
|
22
|
+
* 1. Build notification: { notification: "agent_state_changed", data: { agent, type, agentName, state, reason } }
|
|
23
|
+
* 2. For each connection in routing table: write notification to socket
|
|
24
|
+
* 3. On write error: log notification.dispatch.failed at DEBUG, discard for that connection
|
|
25
|
+
*
|
|
26
|
+
* dispatchAgentCurrentChanged(connectionId, fromAgent, toAgent):
|
|
27
|
+
* 1. Build notification: { notification: "agent_current_changed", data: { agent: toAgent, type, fromAgent, toAgent } }
|
|
28
|
+
* 2. Write to the ONE connection identified by connectionId
|
|
29
|
+
* 3. On write error: log notification.dispatch.failed at DEBUG
|
|
30
|
+
*
|
|
31
|
+
* dispatchSessionStateChanged(agentName, sessionId, state, counterpartyPubkey):
|
|
32
|
+
* 1. Build notification: { notification: "session_state_changed", data: { agent, type, agentName, sessionId, state, counterpartyPubkey } }
|
|
33
|
+
* 2. For each connection where currentAgent === agentName: write notification
|
|
34
|
+
* 3. On write error: log notification.dispatch.failed at DEBUG, discard for that connection
|
|
35
|
+
*/
|
|
36
|
+
import type { Logger, IpcNotification } from "./types.js";
|
|
37
|
+
export type NotificationSender = (connectionId: string, notification: IpcNotification) => boolean;
|
|
38
|
+
export interface NotificationDispatcherConfig {
|
|
39
|
+
logger: Logger;
|
|
40
|
+
sendNotification: NotificationSender;
|
|
41
|
+
getConnectionIds: () => string[];
|
|
42
|
+
}
|
|
43
|
+
export declare class NotificationDispatcher {
|
|
44
|
+
#private;
|
|
45
|
+
constructor(config: NotificationDispatcherConfig);
|
|
46
|
+
registerConnection(connectionId: string): void;
|
|
47
|
+
unregisterConnection(connectionId: string): void;
|
|
48
|
+
setCurrentAgent(connectionId: string, agentName: string | null): void;
|
|
49
|
+
/**
|
|
50
|
+
* Broadcast agent_state_changed to ALL active IPC connections.
|
|
51
|
+
* Routing rule: all connections receive this, including those with no current agent.
|
|
52
|
+
*/
|
|
53
|
+
dispatchAgentStateChanged(agentName: string, state: "online" | "offline", reason: string): void;
|
|
54
|
+
/**
|
|
55
|
+
* Send agent_current_changed to the triggering connection ONLY.
|
|
56
|
+
* Routing rule: only the connection that called cello_use_agent receives this.
|
|
57
|
+
*/
|
|
58
|
+
dispatchAgentCurrentChanged(connectionId: string, fromAgent: string | null, toAgent: string | null): void;
|
|
59
|
+
/**
|
|
60
|
+
* Send session_state_changed to connections where the affected agent is current.
|
|
61
|
+
* Routing rule: only connections with currentAgent === agentName receive this.
|
|
62
|
+
*/
|
|
63
|
+
dispatchSessionStateChanged(agentName: string, sessionId: string, state: string, counterpartyPubkey: string | null): void;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=notification-dispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-dispatcher.d.ts","sourceRoot":"","sources":["../src/notification-dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE1D,MAAM,MAAM,kBAAkB,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,KAAK,OAAO,CAAC;AAElG,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,kBAAkB,CAAC;IACrC,gBAAgB,EAAE,MAAM,MAAM,EAAE,CAAC;CAClC;AAED,qBAAa,sBAAsB;;gBAOrB,MAAM,EAAE,4BAA4B;IAMhD,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAI9C,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAIhD,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAMrE;;;OAGG;IACH,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAkB/F;;;OAGG;IACH,2BAA2B,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAczG;;;OAGG;IACH,2BAA2B,CACzB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,kBAAkB,EAAE,MAAM,GAAG,IAAI,GAChC,IAAI;CAuCR"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO Daemon — NotificationDispatcher
|
|
3
|
+
*
|
|
4
|
+
* Owns the routing table for MCP notifications. Applies per-notification-type
|
|
5
|
+
* routing rules:
|
|
6
|
+
* - agent_state_changed → ALL active IPC connections
|
|
7
|
+
* - agent_current_changed → triggering connection ONLY
|
|
8
|
+
* - session_state_changed → connections where affected agent is current
|
|
9
|
+
*
|
|
10
|
+
* Pseudocode (SPARC Phase P):
|
|
11
|
+
*
|
|
12
|
+
* registerConnection(connectionId, socket):
|
|
13
|
+
* 1. Add entry to routing table: { connectionId → { socket, currentAgent: null } }
|
|
14
|
+
*
|
|
15
|
+
* unregisterConnection(connectionId):
|
|
16
|
+
* 1. Remove entry from routing table
|
|
17
|
+
*
|
|
18
|
+
* setCurrentAgent(connectionId, agentName):
|
|
19
|
+
* 1. Update currentAgent for the connection entry
|
|
20
|
+
*
|
|
21
|
+
* dispatchAgentStateChanged(agentName, state, reason):
|
|
22
|
+
* 1. Build notification: { notification: "agent_state_changed", data: { agent, type, agentName, state, reason } }
|
|
23
|
+
* 2. For each connection in routing table: write notification to socket
|
|
24
|
+
* 3. On write error: log notification.dispatch.failed at DEBUG, discard for that connection
|
|
25
|
+
*
|
|
26
|
+
* dispatchAgentCurrentChanged(connectionId, fromAgent, toAgent):
|
|
27
|
+
* 1. Build notification: { notification: "agent_current_changed", data: { agent: toAgent, type, fromAgent, toAgent } }
|
|
28
|
+
* 2. Write to the ONE connection identified by connectionId
|
|
29
|
+
* 3. On write error: log notification.dispatch.failed at DEBUG
|
|
30
|
+
*
|
|
31
|
+
* dispatchSessionStateChanged(agentName, sessionId, state, counterpartyPubkey):
|
|
32
|
+
* 1. Build notification: { notification: "session_state_changed", data: { agent, type, agentName, sessionId, state, counterpartyPubkey } }
|
|
33
|
+
* 2. For each connection where currentAgent === agentName: write notification
|
|
34
|
+
* 3. On write error: log notification.dispatch.failed at DEBUG, discard for that connection
|
|
35
|
+
*/
|
|
36
|
+
export class NotificationDispatcher {
|
|
37
|
+
#logger;
|
|
38
|
+
#sendNotification;
|
|
39
|
+
#getConnectionIds;
|
|
40
|
+
// Maps connectionId → currentAgent name (null if not set)
|
|
41
|
+
#currentAgentMap = new Map();
|
|
42
|
+
constructor(config) {
|
|
43
|
+
this.#logger = config.logger;
|
|
44
|
+
this.#sendNotification = config.sendNotification;
|
|
45
|
+
this.#getConnectionIds = config.getConnectionIds;
|
|
46
|
+
}
|
|
47
|
+
registerConnection(connectionId) {
|
|
48
|
+
this.#currentAgentMap.set(connectionId, null);
|
|
49
|
+
}
|
|
50
|
+
unregisterConnection(connectionId) {
|
|
51
|
+
this.#currentAgentMap.delete(connectionId);
|
|
52
|
+
}
|
|
53
|
+
setCurrentAgent(connectionId, agentName) {
|
|
54
|
+
if (this.#currentAgentMap.has(connectionId)) {
|
|
55
|
+
this.#currentAgentMap.set(connectionId, agentName);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Broadcast agent_state_changed to ALL active IPC connections.
|
|
60
|
+
* Routing rule: all connections receive this, including those with no current agent.
|
|
61
|
+
*/
|
|
62
|
+
dispatchAgentStateChanged(agentName, state, reason) {
|
|
63
|
+
const notification = {
|
|
64
|
+
notification: "agent_state_changed",
|
|
65
|
+
data: {
|
|
66
|
+
agent: agentName,
|
|
67
|
+
type: "agent_state_changed",
|
|
68
|
+
agentName,
|
|
69
|
+
state,
|
|
70
|
+
reason,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
for (const connectionId of this.#getConnectionIds()) {
|
|
74
|
+
if (!this.#currentAgentMap.has(connectionId))
|
|
75
|
+
continue;
|
|
76
|
+
this.#safeSend(connectionId, notification, "agent_state_changed");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Send agent_current_changed to the triggering connection ONLY.
|
|
81
|
+
* Routing rule: only the connection that called cello_use_agent receives this.
|
|
82
|
+
*/
|
|
83
|
+
dispatchAgentCurrentChanged(connectionId, fromAgent, toAgent) {
|
|
84
|
+
const notification = {
|
|
85
|
+
notification: "agent_current_changed",
|
|
86
|
+
data: {
|
|
87
|
+
agent: toAgent,
|
|
88
|
+
type: "agent_current_changed",
|
|
89
|
+
fromAgent,
|
|
90
|
+
toAgent,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
this.#safeSend(connectionId, notification, "agent_current_changed");
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Send session_state_changed to connections where the affected agent is current.
|
|
97
|
+
* Routing rule: only connections with currentAgent === agentName receive this.
|
|
98
|
+
*/
|
|
99
|
+
dispatchSessionStateChanged(agentName, sessionId, state, counterpartyPubkey) {
|
|
100
|
+
const notification = {
|
|
101
|
+
notification: "session_state_changed",
|
|
102
|
+
data: {
|
|
103
|
+
agent: agentName,
|
|
104
|
+
type: "session_state_changed",
|
|
105
|
+
agentName,
|
|
106
|
+
sessionId,
|
|
107
|
+
state,
|
|
108
|
+
counterpartyPubkey,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
for (const connectionId of this.#getConnectionIds()) {
|
|
112
|
+
const currentAgent = this.#currentAgentMap.get(connectionId);
|
|
113
|
+
if (currentAgent === agentName) {
|
|
114
|
+
this.#safeSend(connectionId, notification, "session_state_changed");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
#safeSend(connectionId, notification, notificationType) {
|
|
119
|
+
try {
|
|
120
|
+
const success = this.#sendNotification(connectionId, notification);
|
|
121
|
+
if (!success) {
|
|
122
|
+
this.#logger.debug("notification.dispatch.failed", {
|
|
123
|
+
connectionId,
|
|
124
|
+
notificationType,
|
|
125
|
+
error: "sendNotification returned false",
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
this.#logger.debug("notification.dispatch.failed", {
|
|
131
|
+
connectionId,
|
|
132
|
+
notificationType,
|
|
133
|
+
error: err instanceof Error ? err.message : String(err),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=notification-dispatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-dispatcher.js","sourceRoot":"","sources":["../src/notification-dispatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DaemonRegistrationContext — CELLO-M7-REGISTRATION (Action 2, step c part 2).
|
|
3
|
+
*
|
|
4
|
+
* Implements the RegistrationManager's RegistrationContext seam against daemon
|
|
5
|
+
* internals. The one non-trivial piece is the signaling bridge: the client wrote
|
|
6
|
+
* directly to a raw persistent libp2p Stream and ran its own read loop; the
|
|
7
|
+
* daemon's directory signaling lives behind SignalingManager. So this context:
|
|
8
|
+
* - sends frames via SignalingManager.sendRaw (which CBOR/length-prefix-encodes),
|
|
9
|
+
* - registers ONE inbound handler that routes the registration reply frames
|
|
10
|
+
* (dkg_ready / register_success / register_error) to whichever pending
|
|
11
|
+
* resolver RegistrationManager.register() has currently armed.
|
|
12
|
+
*
|
|
13
|
+
* register() drives the two stages strictly sequentially (await dkg_ready, then
|
|
14
|
+
* await register_success), so at most one resolver is armed at any moment — which
|
|
15
|
+
* is why a bare register_error can be routed to "whichever stage is waiting".
|
|
16
|
+
*/
|
|
17
|
+
import type { CelloNode } from "@cello-protocol/transport";
|
|
18
|
+
import type { IThresholdSigner, KeyProvider } from "@cello-protocol/crypto";
|
|
19
|
+
import type { Logger } from "./types.js";
|
|
20
|
+
import type { DaemonRegistrationPersistence } from "./registration-persistence.js";
|
|
21
|
+
import type { RegistrationContext, SignalingSendResult } from "./registration-manager.js";
|
|
22
|
+
/**
|
|
23
|
+
* Structural slice of SignalingManager this context needs. The real
|
|
24
|
+
* SignalingManager satisfies this (status getter; sendRaw → OperationResult,
|
|
25
|
+
* assignable to SignalingSendResult; registerInboundHandler → unregister fn),
|
|
26
|
+
* and a fake satisfies it in tests.
|
|
27
|
+
*/
|
|
28
|
+
export interface SignalingSeam {
|
|
29
|
+
readonly status: string;
|
|
30
|
+
sendRaw(frame: unknown): Promise<SignalingSendResult>;
|
|
31
|
+
registerInboundHandler(handler: (frame: Record<string, unknown>) => void): () => void;
|
|
32
|
+
}
|
|
33
|
+
export declare class DaemonRegistrationContext implements RegistrationContext {
|
|
34
|
+
#private;
|
|
35
|
+
readonly keyProvider: KeyProvider;
|
|
36
|
+
readonly logger: Logger;
|
|
37
|
+
readonly persistence: DaemonRegistrationPersistence | null;
|
|
38
|
+
readonly mlDsaKeyFile: string | undefined;
|
|
39
|
+
constructor(opts: {
|
|
40
|
+
signaling: SignalingSeam;
|
|
41
|
+
getDirectoryNode: () => CelloNode | null;
|
|
42
|
+
getDirectoryEndpoint: () => {
|
|
43
|
+
peer_id: string;
|
|
44
|
+
multiaddrs: string[];
|
|
45
|
+
} | null;
|
|
46
|
+
keyProvider: KeyProvider;
|
|
47
|
+
persistence: DaemonRegistrationPersistence | null;
|
|
48
|
+
logger: Logger;
|
|
49
|
+
mlDsaKeyFile?: string | undefined;
|
|
50
|
+
});
|
|
51
|
+
getNode(): CelloNode | null;
|
|
52
|
+
getMyPubkeyHex(): string | null;
|
|
53
|
+
setMyPubkeyHex(hex: string): void;
|
|
54
|
+
getDirectoryEndpoint(): {
|
|
55
|
+
peer_id: string;
|
|
56
|
+
multiaddrs: string[];
|
|
57
|
+
} | null;
|
|
58
|
+
getThresholdSigner(): IThresholdSigner | undefined;
|
|
59
|
+
setThresholdSigner(signer: IThresholdSigner): void;
|
|
60
|
+
getMyPrimaryPubkey(): Uint8Array | null;
|
|
61
|
+
setMyPrimaryPubkey(pubkey: Uint8Array): void;
|
|
62
|
+
isSignalingConnected(): boolean;
|
|
63
|
+
sendSignalingFrame(frame: Record<string, unknown>): Promise<SignalingSendResult>;
|
|
64
|
+
setPendingDkgReadyResolve(resolve: ((frame: Record<string, unknown>) => void) | null): void;
|
|
65
|
+
setPendingRegisterResolve(resolve: ((frame: Record<string, unknown>) => void) | null): void;
|
|
66
|
+
/** Stop routing inbound frames (call when the owning agent is torn down). */
|
|
67
|
+
dispose(): void;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=registration-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration-context.d.ts","sourceRoot":"","sources":["../src/registration-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AACnF,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAE1F;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACtD,sBAAsB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACvF;AAID,qBAAa,yBAA0B,YAAW,mBAAmB;;IACnE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,6BAA6B,GAAG,IAAI,CAAC;IAC3D,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;gBAa9B,IAAI,EAAE;QAChB,SAAS,EAAE,aAAa,CAAC;QACzB,gBAAgB,EAAE,MAAM,SAAS,GAAG,IAAI,CAAC;QACzC,oBAAoB,EAAE,MAAM;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,EAAE,CAAA;SAAE,GAAG,IAAI,CAAC;QAC7E,WAAW,EAAE,WAAW,CAAC;QACzB,WAAW,EAAE,6BAA6B,GAAG,IAAI,CAAC;QAClD,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KACnC;IAaD,OAAO,IAAI,SAAS,GAAG,IAAI;IAI3B,cAAc,IAAI,MAAM,GAAG,IAAI;IAI/B,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIjC,oBAAoB,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI;IAIxE,kBAAkB,IAAI,gBAAgB,GAAG,SAAS;IAIlD,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;IAIlD,kBAAkB,IAAI,UAAU,GAAG,IAAI;IAIvC,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAM5C,oBAAoB,IAAI,OAAO;IAIzB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAItF,yBAAyB,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI;IAI3F,yBAAyB,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI;IAI3F,6EAA6E;IAC7E,OAAO,IAAI,IAAI;CAuChB"}
|