@cello-protocol/connect 0.0.68 → 0.0.70
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/package.json +4 -13
- package/dist/config.d.ts +0 -30
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -53
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -4
- package/dist/index.js.map +0 -1
- package/dist/lock-file.d.ts +0 -72
- package/dist/lock-file.d.ts.map +0 -1
- package/dist/lock-file.js +0 -270
- package/dist/lock-file.js.map +0 -1
- package/dist/notifications.d.ts +0 -18
- package/dist/notifications.d.ts.map +0 -1
- package/dist/notifications.js +0 -38
- package/dist/notifications.js.map +0 -1
- package/dist/server.d.ts +0 -94
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -521
- package/dist/server.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cello-protocol/connect",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.70",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -9,17 +9,9 @@
|
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"access": "public"
|
|
11
11
|
},
|
|
12
|
-
"main": "./dist/index.js",
|
|
13
|
-
"types": "./dist/index.d.ts",
|
|
14
12
|
"bin": {
|
|
15
13
|
"cello-mcp": "./dist/bin/cello-mcp.js"
|
|
16
14
|
},
|
|
17
|
-
"exports": {
|
|
18
|
-
".": {
|
|
19
|
-
"import": "./dist/index.js",
|
|
20
|
-
"types": "./dist/index.d.ts"
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
15
|
"files": [
|
|
24
16
|
"dist/",
|
|
25
17
|
"package.json",
|
|
@@ -30,16 +22,15 @@
|
|
|
30
22
|
"@libp2p/peer-id": "^6.0.8",
|
|
31
23
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
32
24
|
"zod": "^4.4.2",
|
|
33
|
-
"@cello-protocol/
|
|
34
|
-
"@cello-protocol/transport": "0.0.
|
|
35
|
-
"@cello-protocol/crypto": "0.0.18"
|
|
25
|
+
"@cello-protocol/crypto": "0.0.19",
|
|
26
|
+
"@cello-protocol/transport": "0.0.19"
|
|
36
27
|
},
|
|
37
28
|
"devDependencies": {
|
|
38
29
|
"@claude-flow/testing": "3.0.0-alpha.6",
|
|
39
30
|
"@types/node": "^25.6.2",
|
|
40
31
|
"cbor-x": "^1.6.0",
|
|
41
32
|
"it-length-prefixed": "^10.0.1",
|
|
42
|
-
"@cello-protocol/daemon": "0.0.
|
|
33
|
+
"@cello-protocol/daemon": "0.0.52"
|
|
43
34
|
},
|
|
44
35
|
"scripts": {
|
|
45
36
|
"typecheck": "tsc --build",
|
package/dist/config.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CELLO adapter runtime configuration.
|
|
3
|
-
*
|
|
4
|
-
* AC-003 (REPOSPLIT-002): CELLO_DIRECTORY_URL defaults to the production directory
|
|
5
|
-
* ALB endpoint. No CELLO_RELAY_MULTIADDR constant exists — relay multiaddr is
|
|
6
|
-
* dynamically assigned per-session by the directory.
|
|
7
|
-
*/
|
|
8
|
-
/** Production directory HTTP endpoint (ALB / Route53 — ALB terminates TLS, internal HTTP). */
|
|
9
|
-
export declare const PRODUCTION_DIRECTORY_URL = "http://directory-us1.cello.mygentic.ai";
|
|
10
|
-
/**
|
|
11
|
-
* Resolve the directory URL from the environment, falling back to the
|
|
12
|
-
* production endpoint when CELLO_DIRECTORY_URL is not set.
|
|
13
|
-
*
|
|
14
|
-
* @param env - process.env or a test substitute
|
|
15
|
-
*/
|
|
16
|
-
export declare function resolveDirectoryUrl(env?: Record<string, string | undefined>): string;
|
|
17
|
-
/**
|
|
18
|
-
* Auto-discover the directory multiaddr via GET /bootstrap on the directory HTTP endpoint.
|
|
19
|
-
*
|
|
20
|
-
* Pseudocode (fetch strategy):
|
|
21
|
-
* 1. GET ${directoryUrl}/bootstrap with 5s AbortController timeout
|
|
22
|
-
* 2. On HTTP 200 + JSON { multiaddr: string } containing "/p2p/" → return multiaddr
|
|
23
|
-
* 3. On HTTP non-200, network error, or invalid JSON → return null
|
|
24
|
-
*
|
|
25
|
-
* @param directoryUrl - Base URL of the directory HTTP endpoint
|
|
26
|
-
* @param fetchFn - fetch implementation (injectable for testing)
|
|
27
|
-
* @returns The resolved multiaddr string, or null on failure
|
|
28
|
-
*/
|
|
29
|
-
export declare function fetchBootstrapMultiaddr(directoryUrl: string, fetchFn?: typeof fetch): Promise<string | null>;
|
|
30
|
-
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,8FAA8F;AAC9F,eAAO,MAAM,wBAAwB,2CAA2C,CAAC;AAEjF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,MAAM,CAEjG;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,uBAAuB,CAC3C,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmBxB"}
|
package/dist/config.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CELLO adapter runtime configuration.
|
|
3
|
-
*
|
|
4
|
-
* AC-003 (REPOSPLIT-002): CELLO_DIRECTORY_URL defaults to the production directory
|
|
5
|
-
* ALB endpoint. No CELLO_RELAY_MULTIADDR constant exists — relay multiaddr is
|
|
6
|
-
* dynamically assigned per-session by the directory.
|
|
7
|
-
*/
|
|
8
|
-
/** Production directory HTTP endpoint (ALB / Route53 — ALB terminates TLS, internal HTTP). */
|
|
9
|
-
export const PRODUCTION_DIRECTORY_URL = "http://directory-us1.cello.mygentic.ai";
|
|
10
|
-
/**
|
|
11
|
-
* Resolve the directory URL from the environment, falling back to the
|
|
12
|
-
* production endpoint when CELLO_DIRECTORY_URL is not set.
|
|
13
|
-
*
|
|
14
|
-
* @param env - process.env or a test substitute
|
|
15
|
-
*/
|
|
16
|
-
export function resolveDirectoryUrl(env = process.env) {
|
|
17
|
-
return env["CELLO_DIRECTORY_URL"] ?? PRODUCTION_DIRECTORY_URL;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Auto-discover the directory multiaddr via GET /bootstrap on the directory HTTP endpoint.
|
|
21
|
-
*
|
|
22
|
-
* Pseudocode (fetch strategy):
|
|
23
|
-
* 1. GET ${directoryUrl}/bootstrap with 5s AbortController timeout
|
|
24
|
-
* 2. On HTTP 200 + JSON { multiaddr: string } containing "/p2p/" → return multiaddr
|
|
25
|
-
* 3. On HTTP non-200, network error, or invalid JSON → return null
|
|
26
|
-
*
|
|
27
|
-
* @param directoryUrl - Base URL of the directory HTTP endpoint
|
|
28
|
-
* @param fetchFn - fetch implementation (injectable for testing)
|
|
29
|
-
* @returns The resolved multiaddr string, or null on failure
|
|
30
|
-
*/
|
|
31
|
-
export async function fetchBootstrapMultiaddr(directoryUrl, fetchFn = fetch) {
|
|
32
|
-
const bootstrapUrl = `${directoryUrl.replace(/\/$/, "")}/bootstrap`;
|
|
33
|
-
const ac = new AbortController();
|
|
34
|
-
const timeout = setTimeout(() => ac.abort(), 5000);
|
|
35
|
-
try {
|
|
36
|
-
const resp = await fetchFn(bootstrapUrl, { signal: ac.signal });
|
|
37
|
-
if (!resp.ok) {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
const json = await resp.json();
|
|
41
|
-
if (typeof json.multiaddr === "string" && json.multiaddr.includes("/p2p/")) {
|
|
42
|
-
return json.multiaddr;
|
|
43
|
-
}
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
finally {
|
|
50
|
-
clearTimeout(timeout);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,8FAA8F;AAC9F,MAAM,CAAC,MAAM,wBAAwB,GAAG,wCAAwC,CAAC;AAEjF;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAA0C,OAAO,CAAC,GAAG;IACvF,OAAO,GAAG,CAAC,qBAAqB,CAAC,IAAI,wBAAwB,CAAC;AAChE,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,YAAoB,EACpB,UAAwB,KAAK;IAE7B,MAAM,YAAY,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC;IACpE,MAAM,EAAE,GAAG,IAAI,eAAe,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;IACnD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACb,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAA6B,CAAC;QAC1D,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3E,OAAO,IAAI,CAAC,SAAS,CAAC;QACxB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export { createMcpServer } from "./server.js";
|
|
2
|
-
export { pushChannelNotification, pushSessionRequestNotification } from "./notifications.js";
|
|
3
|
-
export { resolveDirectoryUrl, PRODUCTION_DIRECTORY_URL, fetchBootstrapMultiaddr } from "./config.js";
|
|
4
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export { createMcpServer } from "./server.js";
|
|
2
|
-
export { pushChannelNotification, pushSessionRequestNotification } from "./notifications.js";
|
|
3
|
-
export { resolveDirectoryUrl, PRODUCTION_DIRECTORY_URL, fetchBootstrapMultiaddr } from "./config.js";
|
|
4
|
-
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/lock-file.d.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lock-file.ts — PID lock file manager for single-instance cello-mcp process
|
|
3
|
-
*
|
|
4
|
-
* CELLO-M6B-001: Every new cello-mcp startup kills any prior process holding
|
|
5
|
-
* the same lock file, ensuring exactly one cello-mcp per agent at all times.
|
|
6
|
-
*
|
|
7
|
-
* Pseudocode (Phase P):
|
|
8
|
-
*
|
|
9
|
-
* acquireLockFile(lockFilePath, opts?):
|
|
10
|
-
* 1. Read lockFilePath. If it doesn't exist, skip to step 7.
|
|
11
|
-
* 2. Parse PID from file content (trim whitespace, parseInt).
|
|
12
|
-
* 3. Check if process with that PID is running (kill(priorPid, 0)).
|
|
13
|
-
* - If ESRCH (no such process) → stale lock → skip to step 7.
|
|
14
|
-
* - If EPERM (permission denied) → process exists but owned by another user → warn and continue to step 7.
|
|
15
|
-
* - If process is running and killable → proceed to step 4.
|
|
16
|
-
* 4. Send SIGTERM to prior process.
|
|
17
|
-
* 5. Poll every 100ms for up to 5 seconds, checking if process still exists (kill(priorPid, 0)).
|
|
18
|
-
* 6. If still running after 5s, send SIGKILL. Log at warn level.
|
|
19
|
-
* 7. Create lock file directory (mkdir -p) if needed.
|
|
20
|
-
* 8. Write current process.pid to lockFilePath.
|
|
21
|
-
* 9. Return a cleanup function that removes the lock file.
|
|
22
|
-
*
|
|
23
|
-
* releaseLockFile(lockFilePath):
|
|
24
|
-
* 1. Call fs.unlinkSync(lockFilePath), catch ENOENT silently.
|
|
25
|
-
*
|
|
26
|
-
* Dependency injection for testability:
|
|
27
|
-
* - killFn: (pid: number, signal: number | NodeJS.Signals) => void
|
|
28
|
-
* Defaults to process.kill. Tests inject a mock that throws for EPERM scenarios.
|
|
29
|
-
*/
|
|
30
|
-
/**
|
|
31
|
-
* Get the lock file path for a given agent name.
|
|
32
|
-
* Used by both cello-mcp.ts and tests to ensure path logic is consistent.
|
|
33
|
-
*
|
|
34
|
-
* @param agentName - Optional agent name (M7+ multi-agent support)
|
|
35
|
-
* @param baseDir - Base directory for .cello (defaults to homedir())
|
|
36
|
-
* @returns Absolute path to the lock file
|
|
37
|
-
*/
|
|
38
|
-
export declare function getLockFilePath(agentName: string | null, baseDir?: string): string;
|
|
39
|
-
export interface LockFileOptions {
|
|
40
|
-
/**
|
|
41
|
-
* Injected kill function for testing. Defaults to process.kill.
|
|
42
|
-
* Tests can inject a mock that throws { code: 'EPERM' } to simulate cross-user scenarios.
|
|
43
|
-
*/
|
|
44
|
-
killFn?: (pid: number, signal: number | NodeJS.Signals) => void;
|
|
45
|
-
/**
|
|
46
|
-
* Logger for observability events. If not provided, no events are logged.
|
|
47
|
-
*/
|
|
48
|
-
logger?: {
|
|
49
|
-
info: (event: string, context: Record<string, unknown>) => void;
|
|
50
|
-
warn: (event: string, context: Record<string, unknown>) => void;
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Acquire the PID lock file. If a prior process is running, kill it (SIGTERM → wait → SIGKILL).
|
|
55
|
-
* Returns a cleanup function that releases the lock file on exit.
|
|
56
|
-
*
|
|
57
|
-
* AC-001, AC-002, AC-004, SI-001
|
|
58
|
-
*/
|
|
59
|
-
export declare function acquireLockFile(lockFilePath: string, opts?: LockFileOptions): Promise<() => void>;
|
|
60
|
-
/**
|
|
61
|
-
* Release the lock file. Called on SIGTERM/SIGINT/normal exit.
|
|
62
|
-
* AC-003
|
|
63
|
-
*
|
|
64
|
-
* @param lockFilePath - Path to the lock file to remove
|
|
65
|
-
* @param logger - Optional logger for observability events
|
|
66
|
-
* @param unlinkFn - Injected unlink function for testing (defaults to fs.unlinkSync)
|
|
67
|
-
*/
|
|
68
|
-
export declare function releaseLockFile(lockFilePath: string, logger?: {
|
|
69
|
-
info: (event: string, context: Record<string, unknown>) => void;
|
|
70
|
-
warn: (event: string, context: Record<string, unknown>) => void;
|
|
71
|
-
}, unlinkFn?: (path: string) => void): void;
|
|
72
|
-
//# sourceMappingURL=lock-file.d.ts.map
|
package/dist/lock-file.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lock-file.d.ts","sourceRoot":"","sources":["../src/lock-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAMH;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,SAAY,GAClB,MAAM,CAIR;AAED,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC;IAChE;;OAEG;IACH,MAAM,CAAC,EAAE;QACP,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QAChE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;KACjE,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,eAAe,GACrB,OAAO,CAAC,MAAM,IAAI,CAAC,CAgMrB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE;IACP,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAChE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACjE,EACD,QAAQ,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAiB,GAC5C,IAAI,CAcN"}
|
package/dist/lock-file.js
DELETED
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lock-file.ts — PID lock file manager for single-instance cello-mcp process
|
|
3
|
-
*
|
|
4
|
-
* CELLO-M6B-001: Every new cello-mcp startup kills any prior process holding
|
|
5
|
-
* the same lock file, ensuring exactly one cello-mcp per agent at all times.
|
|
6
|
-
*
|
|
7
|
-
* Pseudocode (Phase P):
|
|
8
|
-
*
|
|
9
|
-
* acquireLockFile(lockFilePath, opts?):
|
|
10
|
-
* 1. Read lockFilePath. If it doesn't exist, skip to step 7.
|
|
11
|
-
* 2. Parse PID from file content (trim whitespace, parseInt).
|
|
12
|
-
* 3. Check if process with that PID is running (kill(priorPid, 0)).
|
|
13
|
-
* - If ESRCH (no such process) → stale lock → skip to step 7.
|
|
14
|
-
* - If EPERM (permission denied) → process exists but owned by another user → warn and continue to step 7.
|
|
15
|
-
* - If process is running and killable → proceed to step 4.
|
|
16
|
-
* 4. Send SIGTERM to prior process.
|
|
17
|
-
* 5. Poll every 100ms for up to 5 seconds, checking if process still exists (kill(priorPid, 0)).
|
|
18
|
-
* 6. If still running after 5s, send SIGKILL. Log at warn level.
|
|
19
|
-
* 7. Create lock file directory (mkdir -p) if needed.
|
|
20
|
-
* 8. Write current process.pid to lockFilePath.
|
|
21
|
-
* 9. Return a cleanup function that removes the lock file.
|
|
22
|
-
*
|
|
23
|
-
* releaseLockFile(lockFilePath):
|
|
24
|
-
* 1. Call fs.unlinkSync(lockFilePath), catch ENOENT silently.
|
|
25
|
-
*
|
|
26
|
-
* Dependency injection for testability:
|
|
27
|
-
* - killFn: (pid: number, signal: number | NodeJS.Signals) => void
|
|
28
|
-
* Defaults to process.kill. Tests inject a mock that throws for EPERM scenarios.
|
|
29
|
-
*/
|
|
30
|
-
import { readFileSync, writeFileSync, unlinkSync, mkdirSync } from "node:fs";
|
|
31
|
-
import { dirname, join } from "node:path";
|
|
32
|
-
import { homedir } from "node:os";
|
|
33
|
-
/**
|
|
34
|
-
* Get the lock file path for a given agent name.
|
|
35
|
-
* Used by both cello-mcp.ts and tests to ensure path logic is consistent.
|
|
36
|
-
*
|
|
37
|
-
* @param agentName - Optional agent name (M7+ multi-agent support)
|
|
38
|
-
* @param baseDir - Base directory for .cello (defaults to homedir())
|
|
39
|
-
* @returns Absolute path to the lock file
|
|
40
|
-
*/
|
|
41
|
-
export function getLockFilePath(agentName, baseDir = homedir()) {
|
|
42
|
-
return agentName
|
|
43
|
-
? join(baseDir, ".cello", "agents", agentName, "cello-mcp.pid")
|
|
44
|
-
: join(baseDir, ".cello", "cello-mcp.pid");
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Acquire the PID lock file. If a prior process is running, kill it (SIGTERM → wait → SIGKILL).
|
|
48
|
-
* Returns a cleanup function that releases the lock file on exit.
|
|
49
|
-
*
|
|
50
|
-
* AC-001, AC-002, AC-004, SI-001
|
|
51
|
-
*/
|
|
52
|
-
export async function acquireLockFile(lockFilePath, opts) {
|
|
53
|
-
const killFn = opts?.killFn ?? process.kill.bind(process);
|
|
54
|
-
const logger = opts?.logger;
|
|
55
|
-
// Step 1-3: Check for prior process
|
|
56
|
-
let priorPid = null;
|
|
57
|
-
try {
|
|
58
|
-
const content = readFileSync(lockFilePath, "utf8").trim();
|
|
59
|
-
priorPid = parseInt(content, 10);
|
|
60
|
-
if (isNaN(priorPid) || priorPid <= 0) {
|
|
61
|
-
// Invalid or dangerous PID — treat as stale.
|
|
62
|
-
// priorPid === 0: process.kill(0, ...) sends signal to the entire process group.
|
|
63
|
-
// priorPid < 0: process.kill(-1, ...) sends signal to all processes the user can signal.
|
|
64
|
-
// Both are POSIX semantics — neither refers to the intended prior cello-mcp instance.
|
|
65
|
-
priorPid = null;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
catch (err) {
|
|
69
|
-
// ENOENT = no lock file exists → proceed to write
|
|
70
|
-
if (err.code !== "ENOENT") {
|
|
71
|
-
// Unexpected read error (permissions, etc) — non-fatal, log and continue
|
|
72
|
-
logger?.warn("client.startup.lock.read.failed", {
|
|
73
|
-
reason: err.message,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
priorPid = null;
|
|
77
|
-
}
|
|
78
|
-
if (priorPid !== null) {
|
|
79
|
-
// Check if process is running
|
|
80
|
-
let isRunning = false;
|
|
81
|
-
try {
|
|
82
|
-
killFn(priorPid, 0); // signal 0 = check existence without killing
|
|
83
|
-
isRunning = true;
|
|
84
|
-
}
|
|
85
|
-
catch (err) {
|
|
86
|
-
const code = err.code;
|
|
87
|
-
if (code === "ESRCH") {
|
|
88
|
-
// No such process — stale lock
|
|
89
|
-
isRunning = false;
|
|
90
|
-
}
|
|
91
|
-
else if (code === "EPERM") {
|
|
92
|
-
// Process exists but owned by another user (SI-001)
|
|
93
|
-
logger?.warn("client.startup.lock.eperm", {
|
|
94
|
-
priorPid,
|
|
95
|
-
reason: "process owned by another user",
|
|
96
|
-
});
|
|
97
|
-
isRunning = false; // Treat as stale — we cannot kill it, proceed with our own lock
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
// Unknown error — log and treat as not running
|
|
101
|
-
logger?.warn("client.startup.lock.check.failed", {
|
|
102
|
-
priorPid,
|
|
103
|
-
reason: err.message,
|
|
104
|
-
});
|
|
105
|
-
isRunning = false;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
if (isRunning) {
|
|
109
|
-
// Step 4: Send SIGTERM
|
|
110
|
-
// Track whether SIGTERM was successfully delivered. If the kill call throws
|
|
111
|
-
// (race: process exited between the existence check and the kill call), we
|
|
112
|
-
// know the process is already gone — we must NOT emit prior.process.killed,
|
|
113
|
-
// because the taxonomy entry says "after successfully killing a prior process."
|
|
114
|
-
let sigTermDelivered = false;
|
|
115
|
-
try {
|
|
116
|
-
killFn(priorPid, "SIGTERM");
|
|
117
|
-
sigTermDelivered = true;
|
|
118
|
-
}
|
|
119
|
-
catch (err) {
|
|
120
|
-
// Kill failed (race: process exited between check and kill) — non-fatal.
|
|
121
|
-
// The process is already gone; skip the polling phase and proceed to write.
|
|
122
|
-
logger?.warn("client.startup.prior.kill.failed", {
|
|
123
|
-
priorPid,
|
|
124
|
-
signal: "SIGTERM",
|
|
125
|
-
reason: err.message,
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
// Only poll if SIGTERM was actually delivered
|
|
129
|
-
let stillRunning = sigTermDelivered;
|
|
130
|
-
if (sigTermDelivered) {
|
|
131
|
-
// Step 5: Poll for up to 5 seconds
|
|
132
|
-
const startTime = Date.now();
|
|
133
|
-
const timeout = 5000;
|
|
134
|
-
// Check immediately after SIGTERM (most processes exit quickly)
|
|
135
|
-
try {
|
|
136
|
-
killFn(priorPid, 0);
|
|
137
|
-
stillRunning = true;
|
|
138
|
-
}
|
|
139
|
-
catch (err) {
|
|
140
|
-
if (err.code === "ESRCH") {
|
|
141
|
-
// ESRCH = no such process — process is gone
|
|
142
|
-
stillRunning = false;
|
|
143
|
-
}
|
|
144
|
-
// EPERM or unknown — process may still exist; treat as still running
|
|
145
|
-
}
|
|
146
|
-
// Only poll if still running after immediate check
|
|
147
|
-
while (stillRunning && Date.now() - startTime < timeout) {
|
|
148
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
149
|
-
try {
|
|
150
|
-
killFn(priorPid, 0);
|
|
151
|
-
stillRunning = true;
|
|
152
|
-
}
|
|
153
|
-
catch (err) {
|
|
154
|
-
if (err.code === "ESRCH") {
|
|
155
|
-
// ESRCH = no such process — process is gone
|
|
156
|
-
stillRunning = false;
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
// EPERM or unknown — process may still exist; treat as still running
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// Step 6: Escalate to SIGKILL if needed
|
|
164
|
-
if (stillRunning) {
|
|
165
|
-
try {
|
|
166
|
-
killFn(priorPid, "SIGKILL");
|
|
167
|
-
// Poll after SIGKILL to verify the kernel has reaped the process before
|
|
168
|
-
// we write the lock file. SIGKILL is unblockable but process cleanup
|
|
169
|
-
// (releasing file locks, closing fds) is not instantaneous — especially
|
|
170
|
-
// on loaded systems. Poll up to 500ms in 50ms intervals.
|
|
171
|
-
const sigkillDeadline = Date.now() + 500;
|
|
172
|
-
let reaped = false;
|
|
173
|
-
while (Date.now() < sigkillDeadline) {
|
|
174
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
175
|
-
try {
|
|
176
|
-
killFn(priorPid, 0);
|
|
177
|
-
// Still running — keep polling
|
|
178
|
-
}
|
|
179
|
-
catch (err) {
|
|
180
|
-
if (err.code === "ESRCH") {
|
|
181
|
-
// ESRCH = no such process — reaping confirmed
|
|
182
|
-
reaped = true;
|
|
183
|
-
break;
|
|
184
|
-
}
|
|
185
|
-
// EPERM or unknown — not yet confirmed reaped; continue polling
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
// Log a single event regardless of whether reaping was confirmed.
|
|
189
|
-
// reaped=false on a very loaded system means SIGKILL was sent but the kernel
|
|
190
|
-
// hasn't cleaned up within 500ms — the process is dying, we proceed anyway.
|
|
191
|
-
logger?.warn("client.startup.prior.process.killed", {
|
|
192
|
-
priorPid,
|
|
193
|
-
signal: "SIGKILL",
|
|
194
|
-
reaped,
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
catch (err) {
|
|
198
|
-
logger?.warn("client.startup.prior.kill.failed", {
|
|
199
|
-
priorPid,
|
|
200
|
-
signal: "SIGKILL",
|
|
201
|
-
reason: err.message,
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
else if (sigTermDelivered) {
|
|
206
|
-
// Graceful exit — SIGTERM was delivered and the process exited on its own.
|
|
207
|
-
// Only emit this event when we successfully sent the signal (sigTermDelivered=true)
|
|
208
|
-
// and the process is confirmed gone (stillRunning=false). Do NOT emit when
|
|
209
|
-
// SIGTERM threw (the process was already dead before we could send the signal).
|
|
210
|
-
logger?.info("client.startup.prior.process.killed", {
|
|
211
|
-
priorPid,
|
|
212
|
-
signal: "SIGTERM",
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
// Step 7-8: Write own PID to lock file
|
|
218
|
-
// Per canonical taxonomy and story observability spec: lock file write failure is
|
|
219
|
-
// NON-FATAL. Log at warn level and proceed — orphan detection is disabled but the
|
|
220
|
-
// process can still serve MCP tool calls. The operator will see the warn event and
|
|
221
|
-
// can diagnose the filesystem permission issue separately.
|
|
222
|
-
try {
|
|
223
|
-
mkdirSync(dirname(lockFilePath), { recursive: true });
|
|
224
|
-
writeFileSync(lockFilePath, String(process.pid), "utf8");
|
|
225
|
-
logger?.info("client.startup.lock.acquired", { pid: process.pid });
|
|
226
|
-
}
|
|
227
|
-
catch (err) {
|
|
228
|
-
logger?.warn("client.startup.lock.write.failed", {
|
|
229
|
-
reason: err.message,
|
|
230
|
-
});
|
|
231
|
-
// Non-fatal: return a no-op cleanup since we have no lock to release
|
|
232
|
-
return () => { };
|
|
233
|
-
}
|
|
234
|
-
// Step 9: Return idempotent cleanup function.
|
|
235
|
-
// The function may be called from multiple paths (exit event + signal/exception handler).
|
|
236
|
-
// The released flag suppresses the second call to prevent a duplicate
|
|
237
|
-
// client.startup.lock.released log event in the wild (I-3 finding).
|
|
238
|
-
let released = false;
|
|
239
|
-
return () => {
|
|
240
|
-
if (!released) {
|
|
241
|
-
released = true;
|
|
242
|
-
releaseLockFile(lockFilePath, logger);
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Release the lock file. Called on SIGTERM/SIGINT/normal exit.
|
|
248
|
-
* AC-003
|
|
249
|
-
*
|
|
250
|
-
* @param lockFilePath - Path to the lock file to remove
|
|
251
|
-
* @param logger - Optional logger for observability events
|
|
252
|
-
* @param unlinkFn - Injected unlink function for testing (defaults to fs.unlinkSync)
|
|
253
|
-
*/
|
|
254
|
-
export function releaseLockFile(lockFilePath, logger, unlinkFn = unlinkSync) {
|
|
255
|
-
try {
|
|
256
|
-
unlinkFn(lockFilePath);
|
|
257
|
-
logger?.info("client.startup.lock.released", { pid: process.pid });
|
|
258
|
-
}
|
|
259
|
-
catch (err) {
|
|
260
|
-
// ENOENT is expected if cleanup ran twice — silent
|
|
261
|
-
if (err.code !== "ENOENT") {
|
|
262
|
-
// Unexpected unlink failure (EACCES, EROFS, etc.) — non-fatal but observable
|
|
263
|
-
logger?.warn("client.startup.lock.release.failed", {
|
|
264
|
-
reason: err.message,
|
|
265
|
-
pid: process.pid,
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
//# sourceMappingURL=lock-file.js.map
|
package/dist/lock-file.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lock-file.js","sourceRoot":"","sources":["../src/lock-file.ts"],"names":[],"mappings":"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"}
|
package/dist/notifications.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
/**
|
|
3
|
-
* Push a claude/channel wake-up notification to the connected Claude Code session.
|
|
4
|
-
* Message content is never included — the agent calls cello_receive to retrieve it. The payload
|
|
5
|
-
* is Claude Code's channel contract `{ content, meta }` (see channel-params.ts): `content` is a
|
|
6
|
-
* fixed, content-free doorbell announcement (NOT message text), `meta` carries only type + sender
|
|
7
|
-
* pubkey. SI-001 (ADAPTER-001) holds — no message content rides the push.
|
|
8
|
-
*/
|
|
9
|
-
export declare function pushChannelNotification(server: McpServer, from: string): Promise<void>;
|
|
10
|
-
/**
|
|
11
|
-
* Push a cello_session_request claude/channel notification to the connected Claude Code session.
|
|
12
|
-
* Carries only counterparty pubkey and session_id in `meta` — no content, no multiaddrs, no trust
|
|
13
|
-
* data. SI-001 (ADAPTER-002): the payload's routing fields are exactly type, from, and session_id;
|
|
14
|
-
* `content` is a fixed doorbell announcement, never message content. The agent calls
|
|
15
|
-
* cello_await_session to retrieve the full session details.
|
|
16
|
-
*/
|
|
17
|
-
export declare function pushSessionRequestNotification(server: McpServer, from: string, sessionId: string): Promise<void>;
|
|
18
|
-
//# sourceMappingURL=notifications.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS5F;AAED;;;;;;GAMG;AACH,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CASf"}
|
package/dist/notifications.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { buildChannelParams } from "./channel-params.js";
|
|
2
|
-
/**
|
|
3
|
-
* Push a claude/channel wake-up notification to the connected Claude Code session.
|
|
4
|
-
* Message content is never included — the agent calls cello_receive to retrieve it. The payload
|
|
5
|
-
* is Claude Code's channel contract `{ content, meta }` (see channel-params.ts): `content` is a
|
|
6
|
-
* fixed, content-free doorbell announcement (NOT message text), `meta` carries only type + sender
|
|
7
|
-
* pubkey. SI-001 (ADAPTER-001) holds — no message content rides the push.
|
|
8
|
-
*/
|
|
9
|
-
export async function pushChannelNotification(server, from) {
|
|
10
|
-
try {
|
|
11
|
-
await server.server.notification({
|
|
12
|
-
method: "notifications/claude/channel",
|
|
13
|
-
params: buildChannelParams({ type: "cello_message", from }),
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
// Transport may not be connected or may have closed — silently swallow
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Push a cello_session_request claude/channel notification to the connected Claude Code session.
|
|
22
|
-
* Carries only counterparty pubkey and session_id in `meta` — no content, no multiaddrs, no trust
|
|
23
|
-
* data. SI-001 (ADAPTER-002): the payload's routing fields are exactly type, from, and session_id;
|
|
24
|
-
* `content` is a fixed doorbell announcement, never message content. The agent calls
|
|
25
|
-
* cello_await_session to retrieve the full session details.
|
|
26
|
-
*/
|
|
27
|
-
export async function pushSessionRequestNotification(server, from, sessionId) {
|
|
28
|
-
try {
|
|
29
|
-
await server.server.notification({
|
|
30
|
-
method: "notifications/claude/channel",
|
|
31
|
-
params: buildChannelParams({ type: "cello_session_request", from, session_id: sessionId }),
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
// Transport may not be connected or may have closed — silently swallow
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
//# sourceMappingURL=notifications.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.js","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,MAAiB,EAAE,IAAY;IAC3E,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;YAC/B,MAAM,EAAE,8BAA8B;YACtC,MAAM,EAAE,kBAAkB,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;SAC5D,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;IACzE,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,MAAiB,EACjB,IAAY,EACZ,SAAiB;IAEjB,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;YAC/B,MAAM,EAAE,8BAA8B;YACtC,MAAM,EAAE,kBAAkB,CAAC,EAAE,IAAI,EAAE,uBAAuB,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;SAC3F,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;IACzE,CAAC;AACH,CAAC"}
|
package/dist/server.d.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CELLO Adapter — server.ts (ADAPTER-002 / M1)
|
|
3
|
-
*
|
|
4
|
-
* createMcpServer(node, client, keyProvider): McpServer
|
|
5
|
-
* Registers the M1 tool set against a CelloClient.
|
|
6
|
-
* Transport-agnostic: identical tool names, schemas, and wiring under
|
|
7
|
-
* InMemoryTransport (tests) and stdio (production). AC-007.
|
|
8
|
-
*
|
|
9
|
-
* PSEUDOCODE (Phase P — ADAPTER-002):
|
|
10
|
-
*
|
|
11
|
-
* State held by the MCP server instance:
|
|
12
|
-
* sessionEventQueue: Array<InboundSessionEvent> FIFO; populated by onSessionAssignment handler
|
|
13
|
-
* sessionEventResolvers: Array<(event: InboundSessionEvent) => void> blocked cello_await_session waiters
|
|
14
|
-
* startedAt: number = Date.now()
|
|
15
|
-
*
|
|
16
|
-
* Session event enqueue (onSessionAssignment handler):
|
|
17
|
-
* Receives: SessionAssignmentEvent { sessionIdHex, counterpartyPubkeyHex, genesisPrevRootHex }
|
|
18
|
-
* (from CelloClient.onSessionAssignment per CELLO-MCP-002; fields are pre-encoded as hex)
|
|
19
|
-
* 1. Push cello_session_request notification via claude/channel (SI-001: only type, from, session_id)
|
|
20
|
-
* 2. If sessionEventResolvers.length > 0:
|
|
21
|
-
* - shift the first resolver and call it with the event (wake up blocked cello_await_session)
|
|
22
|
-
* 3. Else:
|
|
23
|
-
* - Push event to sessionEventQueue
|
|
24
|
-
*
|
|
25
|
-
* tool: cello_initiate_session({ target_pubkey: string })
|
|
26
|
-
* Delegates to CelloClient.initiateSession (added by MCP-002).
|
|
27
|
-
* Returns { ok: true, session_id: hex } or { ok: false, reason: string }.
|
|
28
|
-
* Stubbed if method not present on client.
|
|
29
|
-
*
|
|
30
|
-
* tool: cello_await_session({ timeout_ms: number })
|
|
31
|
-
* 1. If sessionEventQueue.length > 0:
|
|
32
|
-
* - shift first event
|
|
33
|
-
* - return { type: 'new_session', session_id, counterparty_pubkey, genesis_prev_root }
|
|
34
|
-
* 2. Else: block until event arrives or timeout expires:
|
|
35
|
-
* - Create a Promise that resolves when an event arrives or deadline fires
|
|
36
|
-
* - Push a resolver into sessionEventResolvers
|
|
37
|
-
* - Race: event arrival vs setTimeout(timeout_ms)
|
|
38
|
-
* - On arrival: return new_session
|
|
39
|
-
* - On timeout: remove resolver from array; return { type: 'timeout' }
|
|
40
|
-
*
|
|
41
|
-
* tool: cello_send({ session_id: string, content: string })
|
|
42
|
-
* 1. Guard: transport_not_started
|
|
43
|
-
* 2. UTF-8 encode content → contentBytes
|
|
44
|
-
* 3. client.sendMessage(session_id, contentBytes) → SendMessageResult
|
|
45
|
-
* 4. Map to MCP output:
|
|
46
|
-
* ok:true → { delivered: true }
|
|
47
|
-
* ok:false → { delivered: false, reason: result.reason }
|
|
48
|
-
*
|
|
49
|
-
* tool: cello_receive_session({ session_id: string, timeout_ms: number })
|
|
50
|
-
* 1. Guard: transport_not_started
|
|
51
|
-
* 2. deadline = Date.now() + timeout_ms
|
|
52
|
-
* 3. Poll every 20ms until deadline:
|
|
53
|
-
* a. msg = client.receiveMessage(session_id)
|
|
54
|
-
* b. if msg: return formatted message result
|
|
55
|
-
* c. await sleep(Math.min(20, remaining))
|
|
56
|
-
* 4. return { type: 'timeout' }
|
|
57
|
-
*
|
|
58
|
-
* tool: cello_close_session({ session_id: string })
|
|
59
|
-
* client.closeSession(session_id)
|
|
60
|
-
* return { closed: true }
|
|
61
|
-
*
|
|
62
|
-
* tool: cello_list_sessions()
|
|
63
|
-
* return client.listSessions() mapped to wire shape
|
|
64
|
-
*
|
|
65
|
-
* tool: cello_get_sealed_receipt({ session_id: string })
|
|
66
|
-
* Stubbed in M1: return { available: false, reason: 'not_yet_sealed' }
|
|
67
|
-
* (SESSION-003 implements real seals)
|
|
68
|
-
*
|
|
69
|
-
* tool: cello_get_inclusion_proof({ session_id: string, content_hash: string })
|
|
70
|
-
* Stubbed in M1: return { available: false, reason: 'not_yet_sealed' }
|
|
71
|
-
*
|
|
72
|
-
* tool: cello_status()
|
|
73
|
-
* 1. ownPubkey = Buffer.from(await keyProvider.getPublicKey()).toString('hex')
|
|
74
|
-
* 2. sessions = client.listSessions()
|
|
75
|
-
* 3. return {
|
|
76
|
-
* transport_started: transportStarted(),
|
|
77
|
-
* own_pubkey: ownPubkey,
|
|
78
|
-
* listen_addresses: node.listenAddresses(),
|
|
79
|
-
* connected_peer_count: node.getConnections().length,
|
|
80
|
-
* uptime_seconds: Math.floor((Date.now() - startedAt) / 1000),
|
|
81
|
-
* active_session_count: sessions.filter(s => s.status === 'active').length,
|
|
82
|
-
* directory_reachable: false // M1 stub — directory reachability check deferred
|
|
83
|
-
* }
|
|
84
|
-
*/
|
|
85
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
86
|
-
import type { CelloClient, ClientBackup } from "@cello-protocol/client";
|
|
87
|
-
import type { CelloNode } from "@cello-protocol/transport";
|
|
88
|
-
import type { KeyProvider } from "@cello-protocol/crypto";
|
|
89
|
-
import type { CheckpointStatusProvider } from "@cello-protocol/interfaces";
|
|
90
|
-
export declare function createMcpServer(node: CelloNode, client: CelloClient, keyProvider: KeyProvider, opts?: {
|
|
91
|
-
checkpointStatusProvider?: CheckpointStatusProvider;
|
|
92
|
-
clientBackup?: ClientBackup;
|
|
93
|
-
}): McpServer;
|
|
94
|
-
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmFG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,KAAK,EAAE,WAAW,EAA0B,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAChG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAoB3E,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,WAAW,EACxB,IAAI,CAAC,EAAE;IAAE,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IAAC,YAAY,CAAC,EAAE,YAAY,CAAA;CAAE,GAC1F,SAAS,CAofX"}
|
package/dist/server.js
DELETED
|
@@ -1,521 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CELLO Adapter — server.ts (ADAPTER-002 / M1)
|
|
3
|
-
*
|
|
4
|
-
* createMcpServer(node, client, keyProvider): McpServer
|
|
5
|
-
* Registers the M1 tool set against a CelloClient.
|
|
6
|
-
* Transport-agnostic: identical tool names, schemas, and wiring under
|
|
7
|
-
* InMemoryTransport (tests) and stdio (production). AC-007.
|
|
8
|
-
*
|
|
9
|
-
* PSEUDOCODE (Phase P — ADAPTER-002):
|
|
10
|
-
*
|
|
11
|
-
* State held by the MCP server instance:
|
|
12
|
-
* sessionEventQueue: Array<InboundSessionEvent> FIFO; populated by onSessionAssignment handler
|
|
13
|
-
* sessionEventResolvers: Array<(event: InboundSessionEvent) => void> blocked cello_await_session waiters
|
|
14
|
-
* startedAt: number = Date.now()
|
|
15
|
-
*
|
|
16
|
-
* Session event enqueue (onSessionAssignment handler):
|
|
17
|
-
* Receives: SessionAssignmentEvent { sessionIdHex, counterpartyPubkeyHex, genesisPrevRootHex }
|
|
18
|
-
* (from CelloClient.onSessionAssignment per CELLO-MCP-002; fields are pre-encoded as hex)
|
|
19
|
-
* 1. Push cello_session_request notification via claude/channel (SI-001: only type, from, session_id)
|
|
20
|
-
* 2. If sessionEventResolvers.length > 0:
|
|
21
|
-
* - shift the first resolver and call it with the event (wake up blocked cello_await_session)
|
|
22
|
-
* 3. Else:
|
|
23
|
-
* - Push event to sessionEventQueue
|
|
24
|
-
*
|
|
25
|
-
* tool: cello_initiate_session({ target_pubkey: string })
|
|
26
|
-
* Delegates to CelloClient.initiateSession (added by MCP-002).
|
|
27
|
-
* Returns { ok: true, session_id: hex } or { ok: false, reason: string }.
|
|
28
|
-
* Stubbed if method not present on client.
|
|
29
|
-
*
|
|
30
|
-
* tool: cello_await_session({ timeout_ms: number })
|
|
31
|
-
* 1. If sessionEventQueue.length > 0:
|
|
32
|
-
* - shift first event
|
|
33
|
-
* - return { type: 'new_session', session_id, counterparty_pubkey, genesis_prev_root }
|
|
34
|
-
* 2. Else: block until event arrives or timeout expires:
|
|
35
|
-
* - Create a Promise that resolves when an event arrives or deadline fires
|
|
36
|
-
* - Push a resolver into sessionEventResolvers
|
|
37
|
-
* - Race: event arrival vs setTimeout(timeout_ms)
|
|
38
|
-
* - On arrival: return new_session
|
|
39
|
-
* - On timeout: remove resolver from array; return { type: 'timeout' }
|
|
40
|
-
*
|
|
41
|
-
* tool: cello_send({ session_id: string, content: string })
|
|
42
|
-
* 1. Guard: transport_not_started
|
|
43
|
-
* 2. UTF-8 encode content → contentBytes
|
|
44
|
-
* 3. client.sendMessage(session_id, contentBytes) → SendMessageResult
|
|
45
|
-
* 4. Map to MCP output:
|
|
46
|
-
* ok:true → { delivered: true }
|
|
47
|
-
* ok:false → { delivered: false, reason: result.reason }
|
|
48
|
-
*
|
|
49
|
-
* tool: cello_receive_session({ session_id: string, timeout_ms: number })
|
|
50
|
-
* 1. Guard: transport_not_started
|
|
51
|
-
* 2. deadline = Date.now() + timeout_ms
|
|
52
|
-
* 3. Poll every 20ms until deadline:
|
|
53
|
-
* a. msg = client.receiveMessage(session_id)
|
|
54
|
-
* b. if msg: return formatted message result
|
|
55
|
-
* c. await sleep(Math.min(20, remaining))
|
|
56
|
-
* 4. return { type: 'timeout' }
|
|
57
|
-
*
|
|
58
|
-
* tool: cello_close_session({ session_id: string })
|
|
59
|
-
* client.closeSession(session_id)
|
|
60
|
-
* return { closed: true }
|
|
61
|
-
*
|
|
62
|
-
* tool: cello_list_sessions()
|
|
63
|
-
* return client.listSessions() mapped to wire shape
|
|
64
|
-
*
|
|
65
|
-
* tool: cello_get_sealed_receipt({ session_id: string })
|
|
66
|
-
* Stubbed in M1: return { available: false, reason: 'not_yet_sealed' }
|
|
67
|
-
* (SESSION-003 implements real seals)
|
|
68
|
-
*
|
|
69
|
-
* tool: cello_get_inclusion_proof({ session_id: string, content_hash: string })
|
|
70
|
-
* Stubbed in M1: return { available: false, reason: 'not_yet_sealed' }
|
|
71
|
-
*
|
|
72
|
-
* tool: cello_status()
|
|
73
|
-
* 1. ownPubkey = Buffer.from(await keyProvider.getPublicKey()).toString('hex')
|
|
74
|
-
* 2. sessions = client.listSessions()
|
|
75
|
-
* 3. return {
|
|
76
|
-
* transport_started: transportStarted(),
|
|
77
|
-
* own_pubkey: ownPubkey,
|
|
78
|
-
* listen_addresses: node.listenAddresses(),
|
|
79
|
-
* connected_peer_count: node.getConnections().length,
|
|
80
|
-
* uptime_seconds: Math.floor((Date.now() - startedAt) / 1000),
|
|
81
|
-
* active_session_count: sessions.filter(s => s.status === 'active').length,
|
|
82
|
-
* directory_reachable: false // M1 stub — directory reachability check deferred
|
|
83
|
-
* }
|
|
84
|
-
*/
|
|
85
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
86
|
-
import { z } from "zod";
|
|
87
|
-
import { pushSessionRequestNotification } from "./notifications.js";
|
|
88
|
-
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
89
|
-
function jsonText(value) {
|
|
90
|
-
return { content: [{ type: "text", text: JSON.stringify(value) }] };
|
|
91
|
-
}
|
|
92
|
-
const TRANSPORT_NOT_STARTED = jsonText({ error: { reason: "transport_not_started" } });
|
|
93
|
-
const CLIENT_NOT_INITIALIZED = jsonText({ error: { reason: "client_not_initialized" } });
|
|
94
|
-
// ─── createMcpServer ─────────────────────────────────────────────────────────
|
|
95
|
-
export function createMcpServer(node, client, keyProvider, opts) {
|
|
96
|
-
const checkpointStatusProvider = opts?.checkpointStatusProvider;
|
|
97
|
-
const clientBackup = opts?.clientBackup;
|
|
98
|
-
const startedAt = Date.now();
|
|
99
|
-
// ─── Session event queue ─────────────────────────────────────────────────
|
|
100
|
-
const sessionEventQueue = [];
|
|
101
|
-
const sessionEventResolvers = [];
|
|
102
|
-
function transportStarted() {
|
|
103
|
-
return node.listenAddresses().length > 0;
|
|
104
|
-
}
|
|
105
|
-
const server = new McpServer({ name: "cello", version: "0.1.0" }, { capabilities: { experimental: { "claude/channel": {} } } });
|
|
106
|
-
// ─── Register onSessionAssignment handler ───────────────────────────────
|
|
107
|
-
if (client != null && typeof client.onSessionAssignment === "function") {
|
|
108
|
-
client.onSessionAssignment((event) => {
|
|
109
|
-
void pushSessionRequestNotification(server, event.counterpartyPubkeyHex, event.sessionIdHex);
|
|
110
|
-
if (sessionEventResolvers.length > 0) {
|
|
111
|
-
const resolve = sessionEventResolvers.shift();
|
|
112
|
-
resolve(event);
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
sessionEventQueue.push(event);
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
// ── cello_initiate_session ────────────────────────────────────────────────
|
|
120
|
-
server.registerTool("cello_initiate_session", {
|
|
121
|
-
description: "Initiate a CELLO session with a target agent identified by their public key.",
|
|
122
|
-
inputSchema: {
|
|
123
|
-
target_pubkey: z.string().describe("Target agent public key hex (K_local pubkey of the peer)"),
|
|
124
|
-
},
|
|
125
|
-
}, async ({ target_pubkey }) => {
|
|
126
|
-
if (!transportStarted())
|
|
127
|
-
return TRANSPORT_NOT_STARTED;
|
|
128
|
-
const result = await client.initiateSession(target_pubkey);
|
|
129
|
-
if (result.ok) {
|
|
130
|
-
return jsonText({
|
|
131
|
-
ok: true,
|
|
132
|
-
session_id: Buffer.from(result.sessionId).toString("hex"),
|
|
133
|
-
genesis_prev_root: Buffer.from(result.genesisPrevRoot).toString("hex"),
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
return jsonText({ ok: false, reason: result.reason });
|
|
137
|
-
});
|
|
138
|
-
// ── cello_await_session ───────────────────────────────────────────────────
|
|
139
|
-
server.registerTool("cello_await_session", {
|
|
140
|
-
description: "Wait for an inbound session request. Returns immediately if one is already queued, or blocks until one arrives or the timeout expires.",
|
|
141
|
-
inputSchema: {
|
|
142
|
-
timeout_ms: z.number().int().min(0).describe("Maximum wait time in milliseconds"),
|
|
143
|
-
},
|
|
144
|
-
}, async ({ timeout_ms }) => {
|
|
145
|
-
// If a queued event exists, return it immediately (FIFO)
|
|
146
|
-
if (sessionEventQueue.length > 0) {
|
|
147
|
-
const event = sessionEventQueue.shift();
|
|
148
|
-
return jsonText({
|
|
149
|
-
type: "new_session",
|
|
150
|
-
session_id: event.sessionIdHex,
|
|
151
|
-
counterparty_pubkey: event.counterpartyPubkeyHex,
|
|
152
|
-
genesis_prev_root: event.genesisPrevRootHex,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
// Block until an event arrives or timeout fires
|
|
156
|
-
const result = await new Promise((resolve) => {
|
|
157
|
-
let timerId;
|
|
158
|
-
function resolveEvent(event) {
|
|
159
|
-
clearTimeout(timerId);
|
|
160
|
-
resolve(event);
|
|
161
|
-
}
|
|
162
|
-
sessionEventResolvers.push(resolveEvent);
|
|
163
|
-
timerId = setTimeout(() => {
|
|
164
|
-
const idx = sessionEventResolvers.indexOf(resolveEvent);
|
|
165
|
-
if (idx !== -1)
|
|
166
|
-
sessionEventResolvers.splice(idx, 1);
|
|
167
|
-
resolve(null);
|
|
168
|
-
}, timeout_ms);
|
|
169
|
-
});
|
|
170
|
-
if (result === null) {
|
|
171
|
-
return jsonText({ type: "timeout" });
|
|
172
|
-
}
|
|
173
|
-
return jsonText({
|
|
174
|
-
type: "new_session",
|
|
175
|
-
session_id: result.sessionIdHex,
|
|
176
|
-
counterparty_pubkey: result.counterpartyPubkeyHex,
|
|
177
|
-
genesis_prev_root: result.genesisPrevRootHex,
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
// ── cello_send (session-keyed, M1) ────────────────────────────────────────
|
|
181
|
-
server.registerTool("cello_send", {
|
|
182
|
-
description: "Send a UTF-8 message on an active CELLO session.",
|
|
183
|
-
inputSchema: {
|
|
184
|
-
session_id: z.string().describe("Session ID hex (from cello_initiate_session or cello_await_session)"),
|
|
185
|
-
content: z.string().describe("UTF-8 message content"),
|
|
186
|
-
},
|
|
187
|
-
}, async ({ session_id, content }) => {
|
|
188
|
-
if (client == null)
|
|
189
|
-
return CLIENT_NOT_INITIALIZED;
|
|
190
|
-
if (!transportStarted())
|
|
191
|
-
return TRANSPORT_NOT_STARTED;
|
|
192
|
-
const contentBytes = new TextEncoder().encode(content);
|
|
193
|
-
const result = await client.sendMessage(session_id, contentBytes);
|
|
194
|
-
if (result.ok) {
|
|
195
|
-
return jsonText({ delivered: true });
|
|
196
|
-
}
|
|
197
|
-
return jsonText({ delivered: false, reason: result.reason });
|
|
198
|
-
});
|
|
199
|
-
// ── cello_receive_session (session-keyed, M1) ────────────────────────────
|
|
200
|
-
server.registerTool("cello_receive_session", {
|
|
201
|
-
description: "Wait for a message on a specific CELLO session (session-locked). Blocks until a message arrives or the timeout expires.",
|
|
202
|
-
inputSchema: {
|
|
203
|
-
session_id: z
|
|
204
|
-
.string()
|
|
205
|
-
.describe("Session ID hex (from cello_initiate_session or cello_await_session)"),
|
|
206
|
-
timeout_ms: z.number().int().min(0).describe("Maximum wait time in milliseconds"),
|
|
207
|
-
},
|
|
208
|
-
}, async ({ session_id, timeout_ms }) => {
|
|
209
|
-
if (client == null)
|
|
210
|
-
return CLIENT_NOT_INITIALIZED;
|
|
211
|
-
if (!transportStarted())
|
|
212
|
-
return TRANSPORT_NOT_STARTED;
|
|
213
|
-
const deadline = Date.now() + timeout_ms;
|
|
214
|
-
while (Date.now() < deadline) {
|
|
215
|
-
const msg = client.receiveMessage(session_id);
|
|
216
|
-
if (msg) {
|
|
217
|
-
if (msg.type === "counterparty_closing") {
|
|
218
|
-
return jsonText({
|
|
219
|
-
type: "counterparty_closing",
|
|
220
|
-
session_id: msg.sessionIdHex,
|
|
221
|
-
guidance: "The other party has initiated session closure. Call cello_close_session with this session_id to complete the bilateral seal ceremony. You cannot send further messages on this session.",
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
if (msg.type === "session_sealed") {
|
|
225
|
-
return jsonText({
|
|
226
|
-
type: "session_sealed",
|
|
227
|
-
session_id: msg.sessionIdHex,
|
|
228
|
-
sealed_root: Buffer.from(msg.sealedRoot).toString("hex"),
|
|
229
|
-
close_timestamp: msg.closeTimestamp,
|
|
230
|
-
checkpoint_status: msg.checkpointStatus,
|
|
231
|
-
guidance: "The session has been fully sealed by both parties. No further actions are required for this session.",
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
return jsonText(formatSessionMessage(msg, session_id));
|
|
235
|
-
}
|
|
236
|
-
const remaining = deadline - Date.now();
|
|
237
|
-
if (remaining <= 0)
|
|
238
|
-
break;
|
|
239
|
-
await sleep(Math.min(20, remaining));
|
|
240
|
-
}
|
|
241
|
-
return jsonText({ type: "timeout" });
|
|
242
|
-
});
|
|
243
|
-
// ── cello_receive (any-session, default) ──────────────────────────────────
|
|
244
|
-
server.registerTool("cello_receive", {
|
|
245
|
-
description: "Wait for a message from any active CELLO session (default receive). Returns session_id so caller knows which session the message came from. Blocks until a message arrives or the timeout expires.",
|
|
246
|
-
inputSchema: {
|
|
247
|
-
timeout_ms: z.number().int().min(0).describe("Maximum wait time in milliseconds"),
|
|
248
|
-
},
|
|
249
|
-
}, async ({ timeout_ms }) => {
|
|
250
|
-
if (client == null)
|
|
251
|
-
return CLIENT_NOT_INITIALIZED;
|
|
252
|
-
if (!transportStarted())
|
|
253
|
-
return TRANSPORT_NOT_STARTED;
|
|
254
|
-
const result = await client.receiveMessageAsync(timeout_ms);
|
|
255
|
-
if (result.type === "timeout") {
|
|
256
|
-
return jsonText({ type: "timeout" });
|
|
257
|
-
}
|
|
258
|
-
if (result.type === "counterparty_closing") {
|
|
259
|
-
return jsonText({
|
|
260
|
-
type: "counterparty_closing",
|
|
261
|
-
session_id: result.sessionIdHex,
|
|
262
|
-
guidance: "The other party has initiated session closure. Call cello_close_session with this session_id to complete the bilateral seal ceremony. You cannot send further messages on this session.",
|
|
263
|
-
...(result.otherSessionsPending && result.otherSessionsPending.length > 0
|
|
264
|
-
? { other_sessions_pending: result.otherSessionsPending }
|
|
265
|
-
: {}),
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
if (result.type === "session_sealed") {
|
|
269
|
-
return jsonText({
|
|
270
|
-
type: "session_sealed",
|
|
271
|
-
session_id: result.sessionIdHex,
|
|
272
|
-
sealed_root: Buffer.from(result.sealedRoot).toString("hex"),
|
|
273
|
-
close_timestamp: result.closeTimestamp,
|
|
274
|
-
checkpoint_status: result.checkpointStatus,
|
|
275
|
-
guidance: "The session has been fully sealed by both parties. No further actions are required for this session.",
|
|
276
|
-
...(result.otherSessionsPending && result.otherSessionsPending.length > 0
|
|
277
|
-
? { other_sessions_pending: result.otherSessionsPending }
|
|
278
|
-
: {}),
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
if (result.type === "message") {
|
|
282
|
-
let content;
|
|
283
|
-
try {
|
|
284
|
-
content = new TextDecoder("utf-8", { fatal: true }).decode(result.content);
|
|
285
|
-
}
|
|
286
|
-
catch {
|
|
287
|
-
content = Buffer.from(result.content).toString("hex");
|
|
288
|
-
}
|
|
289
|
-
return jsonText({
|
|
290
|
-
type: "message",
|
|
291
|
-
session_id: result.sessionIdHex,
|
|
292
|
-
content,
|
|
293
|
-
sender_pubkey: Buffer.from(result.senderPubkey).toString("hex"),
|
|
294
|
-
sequence_number: result.sequenceNumber,
|
|
295
|
-
leaf_hash: Buffer.from(result.leafHash).toString("hex"),
|
|
296
|
-
...(result.otherSessionsPending && result.otherSessionsPending.length > 0
|
|
297
|
-
? { other_sessions_pending: result.otherSessionsPending }
|
|
298
|
-
: {}),
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
return jsonText({ type: "unknown", raw: JSON.stringify(result) });
|
|
302
|
-
});
|
|
303
|
-
// ── cello_close_session ───────────────────────────────────────────────────
|
|
304
|
-
server.registerTool("cello_close_session", {
|
|
305
|
-
description: "Close and remove a CELLO session. Idempotent.",
|
|
306
|
-
inputSchema: {
|
|
307
|
-
session_id: z.string().describe("Session ID hex to close"),
|
|
308
|
-
},
|
|
309
|
-
}, async ({ session_id }) => {
|
|
310
|
-
if (client == null)
|
|
311
|
-
return CLIENT_NOT_INITIALIZED;
|
|
312
|
-
// Every completed session must end with a seal — attempt before teardown.
|
|
313
|
-
// If the session is already sealing/sealed/deferred/rejected, skip the attempt.
|
|
314
|
-
const sessions = client.listSessions();
|
|
315
|
-
const session = sessions.find((s) => Buffer.from(s.session_id).toString("hex") === session_id);
|
|
316
|
-
const alreadySealing = session &&
|
|
317
|
-
(session.status === "sealing" || session.status === "sealed" ||
|
|
318
|
-
session.status === "seal_deferred" || session.status === "seal_rejected");
|
|
319
|
-
let sealResult;
|
|
320
|
-
if (!alreadySealing && session?.status === "active") {
|
|
321
|
-
// initiateSessionSeal awaits the full FROST ceremony internally — by the time
|
|
322
|
-
// it returns, the session is already sealed/deferred. No further polling needed.
|
|
323
|
-
sealResult = await client.initiateSessionSeal(session_id);
|
|
324
|
-
}
|
|
325
|
-
client.closeSession(session_id);
|
|
326
|
-
// PERSIST-017: include checkpoint_status if CheckpointStatusProvider is wired in.
|
|
327
|
-
// When not provided (M1 stubs, tests without DB), return closed: true only.
|
|
328
|
-
if (checkpointStatusProvider) {
|
|
329
|
-
const stagingStatus = await checkpointStatusProvider.getSealStagingStatus(session_id);
|
|
330
|
-
if (stagingStatus.status === "pending") {
|
|
331
|
-
return jsonText({ closed: true, checkpoint_status: "pending", staged_at: stagingStatus.staged_at });
|
|
332
|
-
}
|
|
333
|
-
if (stagingStatus.status === "confirmed") {
|
|
334
|
-
return jsonText({
|
|
335
|
-
closed: true,
|
|
336
|
-
checkpoint_status: "confirmed",
|
|
337
|
-
session_mmr_peak: stagingStatus.checkpoint_peak_hash,
|
|
338
|
-
leaf_index: stagingStatus.leaf_index,
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
if (sealResult && !sealResult.ok) {
|
|
343
|
-
return jsonText({ closed: true, seal_status: "failed", seal_reason: sealResult.reason });
|
|
344
|
-
}
|
|
345
|
-
if (sealResult?.ok) {
|
|
346
|
-
return jsonText({ closed: true, seal_status: "initiated" });
|
|
347
|
-
}
|
|
348
|
-
return jsonText({ closed: true });
|
|
349
|
-
});
|
|
350
|
-
// ── cello_list_sessions ───────────────────────────────────────────────────
|
|
351
|
-
server.registerTool("cello_list_sessions", {
|
|
352
|
-
description: "List all current CELLO sessions and their status.",
|
|
353
|
-
inputSchema: {},
|
|
354
|
-
}, async () => {
|
|
355
|
-
if (client == null)
|
|
356
|
-
return CLIENT_NOT_INITIALIZED;
|
|
357
|
-
const sessions = client.listSessions().map((s) => ({
|
|
358
|
-
session_id: Buffer.from(s.session_id).toString("hex"),
|
|
359
|
-
counterparty_pubkey: Buffer.from(s.counterparty_pubkey).toString("hex"),
|
|
360
|
-
status: s.status,
|
|
361
|
-
}));
|
|
362
|
-
return jsonText(sessions);
|
|
363
|
-
});
|
|
364
|
-
// ── cello_get_sealed_receipt ──────────────────────────────────────────────
|
|
365
|
-
// PERSIST-017: returns checkpoint_status when CheckpointStatusProvider is wired.
|
|
366
|
-
// Falls back to the M1 stub response when no provider is configured.
|
|
367
|
-
server.registerTool("cello_get_sealed_receipt", {
|
|
368
|
-
description: "Retrieve the sealed receipt and MMR checkpoint status for a closed session.",
|
|
369
|
-
inputSchema: {
|
|
370
|
-
session_id: z.string().describe("Session ID hex"),
|
|
371
|
-
},
|
|
372
|
-
}, async ({ session_id }) => {
|
|
373
|
-
if (checkpointStatusProvider) {
|
|
374
|
-
const stagingStatus = await checkpointStatusProvider.getSealStagingStatus(session_id);
|
|
375
|
-
if (stagingStatus.status === "pending") {
|
|
376
|
-
return jsonText({
|
|
377
|
-
available: true,
|
|
378
|
-
checkpoint_status: "pending",
|
|
379
|
-
staged_at: stagingStatus.staged_at,
|
|
380
|
-
attestation_self: "PENDING",
|
|
381
|
-
attestation_self_reason: "MMR checkpoint not yet written",
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
if (stagingStatus.status === "confirmed") {
|
|
385
|
-
return jsonText({
|
|
386
|
-
available: true,
|
|
387
|
-
checkpoint_status: "confirmed",
|
|
388
|
-
session_mmr_peak: stagingStatus.checkpoint_peak_hash,
|
|
389
|
-
leaf_index: stagingStatus.leaf_index,
|
|
390
|
-
checkpoint_id: stagingStatus.checkpoint_id,
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
// not_staged: session was never sealed
|
|
394
|
-
return jsonText({ available: false, reason: "not_yet_sealed" });
|
|
395
|
-
}
|
|
396
|
-
return jsonText({ available: false, reason: "not_yet_sealed" });
|
|
397
|
-
});
|
|
398
|
-
// ── cello_get_inclusion_proof ─────────────────────────────────────────────
|
|
399
|
-
// PERSIST-017: returns pending/confirmed status when CheckpointStatusProvider is wired.
|
|
400
|
-
// Falls back to the M1 stub response when no provider is configured.
|
|
401
|
-
server.registerTool("cello_get_inclusion_proof", {
|
|
402
|
-
description: "Retrieve the MMR inclusion proof status for a sealed session.",
|
|
403
|
-
inputSchema: {
|
|
404
|
-
session_id: z.string().describe("Session ID hex"),
|
|
405
|
-
content_hash: z.string().describe("Content hash hex of the message"),
|
|
406
|
-
},
|
|
407
|
-
}, async ({ session_id }) => {
|
|
408
|
-
if (checkpointStatusProvider) {
|
|
409
|
-
const stagingStatus = await checkpointStatusProvider.getSealStagingStatus(session_id);
|
|
410
|
-
if (stagingStatus.status === "pending") {
|
|
411
|
-
return jsonText({
|
|
412
|
-
status: "pending",
|
|
413
|
-
staged_at: stagingStatus.staged_at,
|
|
414
|
-
message: "sealed_root staged; inclusion proof available after next MMR checkpoint",
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
if (stagingStatus.status === "confirmed") {
|
|
418
|
-
return jsonText({
|
|
419
|
-
status: "confirmed",
|
|
420
|
-
leaf_index: stagingStatus.leaf_index,
|
|
421
|
-
checkpoint_peak_hash: stagingStatus.checkpoint_peak_hash,
|
|
422
|
-
checkpoint_id: stagingStatus.checkpoint_id,
|
|
423
|
-
sibling_hashes: stagingStatus.sibling_hashes,
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
// not_staged: session was never sealed — error distinguishes from 'pending'
|
|
427
|
-
return jsonText({ status: "error", reason: "not_yet_sealed" });
|
|
428
|
-
}
|
|
429
|
-
return jsonText({ available: false, reason: "not_yet_sealed" });
|
|
430
|
-
});
|
|
431
|
-
// ── cello_status (extended with M1 fields) ────────────────────────────────
|
|
432
|
-
server.registerTool("cello_status", {
|
|
433
|
-
description: "Return transport status, own pubkey, connection info, and M1 session summary.",
|
|
434
|
-
inputSchema: {},
|
|
435
|
-
}, async () => {
|
|
436
|
-
const ownPubkey = Buffer.from(await keyProvider.getPublicKey()).toString("hex");
|
|
437
|
-
const sessions = client.listSessions();
|
|
438
|
-
const activeSessionCount = sessions.filter((s) => s.status === "active").length;
|
|
439
|
-
// directoryReachable check: any session with a directory_endpoint indicates reachability
|
|
440
|
-
const directoryReachable = sessions.some((s) => s.directory_endpoint && s.directory_endpoint.peer_id !== "");
|
|
441
|
-
return jsonText({
|
|
442
|
-
transport_started: transportStarted(),
|
|
443
|
-
own_pubkey: ownPubkey,
|
|
444
|
-
listen_addresses: node.listenAddresses(),
|
|
445
|
-
connected_peer_count: node.getConnections().length,
|
|
446
|
-
uptime_seconds: Math.floor((Date.now() - startedAt) / 1000),
|
|
447
|
-
active_session_count: activeSessionCount,
|
|
448
|
-
directory_reachable: directoryReachable,
|
|
449
|
-
});
|
|
450
|
-
});
|
|
451
|
-
// ── cello_backup ─────────────────────────────────────────────────────────────
|
|
452
|
-
// PERSIST-022: Trigger an immediate encrypted backup of the local database.
|
|
453
|
-
// If no backup is configured (cloudStorage=null), ClientBackup logs
|
|
454
|
-
// client.backup.not.configured at WARN and returns without error.
|
|
455
|
-
server.registerTool("cello_backup", {
|
|
456
|
-
description: "Trigger an immediate encrypted backup of the local CELLO database to cloud storage. " +
|
|
457
|
-
"Returns ok:true on success. Returns ok:false with reason if backup fails or is not configured.",
|
|
458
|
-
inputSchema: {},
|
|
459
|
-
}, async () => {
|
|
460
|
-
if (!clientBackup) {
|
|
461
|
-
return jsonText({ ok: false, reason: "not_configured" });
|
|
462
|
-
}
|
|
463
|
-
try {
|
|
464
|
-
const result = await clientBackup.backup();
|
|
465
|
-
return jsonText(result);
|
|
466
|
-
}
|
|
467
|
-
catch (err) {
|
|
468
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
469
|
-
return jsonText({ ok: false, reason });
|
|
470
|
-
}
|
|
471
|
-
});
|
|
472
|
-
// ── cello_restore ─────────────────────────────────────────────────────────────
|
|
473
|
-
// PERSIST-022: Download and decrypt the backup, replacing the local database file.
|
|
474
|
-
// On checksum mismatch or decrypt failure the local file is NOT overwritten and restore throws.
|
|
475
|
-
server.registerTool("cello_restore", {
|
|
476
|
-
description: "Restore the local CELLO database from the most recent cloud backup. " +
|
|
477
|
-
"Replaces the local database file only after checksum verification passes. " +
|
|
478
|
-
"Returns ok:true on success. If cloud storage is not configured, returns ok:false.",
|
|
479
|
-
inputSchema: {},
|
|
480
|
-
}, async () => {
|
|
481
|
-
if (!clientBackup) {
|
|
482
|
-
return jsonText({ ok: false, reason: "not_configured" });
|
|
483
|
-
}
|
|
484
|
-
try {
|
|
485
|
-
await clientBackup.restore();
|
|
486
|
-
return jsonText({ ok: true });
|
|
487
|
-
}
|
|
488
|
-
catch (err) {
|
|
489
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
490
|
-
return jsonText({ ok: false, reason });
|
|
491
|
-
}
|
|
492
|
-
});
|
|
493
|
-
return server;
|
|
494
|
-
}
|
|
495
|
-
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
496
|
-
function formatSessionMessage(msg, sessionIdHex) {
|
|
497
|
-
try {
|
|
498
|
-
const content = new TextDecoder("utf-8", { fatal: true }).decode(msg.content);
|
|
499
|
-
return {
|
|
500
|
-
type: "message",
|
|
501
|
-
content,
|
|
502
|
-
session_id: sessionIdHex,
|
|
503
|
-
sender_pubkey: Buffer.from(msg.senderPubkey).toString("hex"),
|
|
504
|
-
sequence_number: msg.sequenceNumber,
|
|
505
|
-
leaf_hash: Buffer.from(msg.leafHash).toString("hex"),
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
catch {
|
|
509
|
-
return {
|
|
510
|
-
type: "decode_error",
|
|
511
|
-
session_id: sessionIdHex,
|
|
512
|
-
sender_pubkey: Buffer.from(msg.senderPubkey).toString("hex"),
|
|
513
|
-
sequence_number: msg.sequenceNumber,
|
|
514
|
-
leaf_hash: Buffer.from(msg.leafHash).toString("hex"),
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
function sleep(ms) {
|
|
519
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
520
|
-
}
|
|
521
|
-
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|