@sleep2agi/commhub-server 0.9.0-preview.21 → 0.9.0-preview.22
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/README.md +1 -1
- package/bin/commhub.ts +6 -2
- package/package.json +1 -1
- package/src/api-host-supervisors-fallback.test.ts +15 -5
- package/src/avatar-validate.test.ts +113 -0
- package/src/avatar-validate.ts +69 -0
- package/src/boot-side-effect.test.ts +124 -0
- package/src/db.ts +10 -0
- package/src/health-redaction.test.ts +176 -0
- package/src/index.ts +11 -2385
- package/src/observer-avatar-http.test.ts +349 -0
- package/src/observer-push.test.ts +176 -0
- package/src/push.ts +87 -7
- package/src/response-charset.test.ts +130 -0
- package/src/server.ts +2642 -0
- package/src/tools.ts +8 -1
- package/src/uploads-http.test.ts +11 -8
package/src/tools.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod/v4";
|
|
3
3
|
import { db, uuidv4, logTaskEvent, chainReplyToParent, hashToken, generateId, generateNetworkToken } from "./db.js";
|
|
4
|
-
import { pushEvent } from "./push.js";
|
|
4
|
+
import { pushEvent, pushNetworkObserverEvent } from "./push.js";
|
|
5
5
|
import { assertNodeActive } from "./lifecycle-guard.js";
|
|
6
6
|
import { getUserNetworkRole, createNetworkTokenForNode } from "./auth.js";
|
|
7
7
|
import {
|
|
@@ -958,6 +958,9 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
958
958
|
if (target.state === "online") {
|
|
959
959
|
pushEvent(targetAlias, { type: "new_task", inbox_count: pending?.cnt ?? 1, priority, from: from_session, ...(canonical.renamed ? { renamed_from: alias } : {}) }, effectiveNetId);
|
|
960
960
|
}
|
|
961
|
+
// #461 network observer summary — unconditional (task row exists
|
|
962
|
+
// even when the target is offline/queued), metadata only.
|
|
963
|
+
pushNetworkObserverEvent(effectiveNetId, { type: "new_task", task_id: id, from: from_session, to: targetAlias, status: target.state === "online" ? "delivered" : "queued", priority });
|
|
961
964
|
|
|
962
965
|
if (target.state === "offline") {
|
|
963
966
|
return {
|
|
@@ -1185,6 +1188,8 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
1185
1188
|
|
|
1186
1189
|
const session = scopedSessionStatus(alias, effectiveNetId);
|
|
1187
1190
|
pushEvent(alias, { type: "new_reply", from: from_session, message_id: id, in_reply_to, status: replyStatus }, effectiveNetId);
|
|
1191
|
+
// #461 network observer summary — ids + routing only, no reply text.
|
|
1192
|
+
pushNetworkObserverEvent(effectiveNetId, { type: "new_reply", task_id: in_reply_to ?? null, message_id: id, from: from_session, to: alias, status: replyStatus });
|
|
1188
1193
|
|
|
1189
1194
|
return {
|
|
1190
1195
|
content: [{
|
|
@@ -1267,6 +1272,7 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
1267
1272
|
logTaskEvent(task_id, task.status, "delivered", from_session, "retry");
|
|
1268
1273
|
// SSE push (unconditional — channel is keyed by alias, not network)
|
|
1269
1274
|
pushEvent(task.to_name, { type: "new_task", inbox_count: 1, priority: task.priority, from: from_session }, effectiveNetId ?? task.network_id ?? null);
|
|
1275
|
+
pushNetworkObserverEvent(effectiveNetId ?? task.network_id ?? null, { type: "new_task", task_id, from: from_session, to: task.to_name, status: "delivered", priority: task.priority });
|
|
1270
1276
|
return {
|
|
1271
1277
|
content: [{ type: "text" as const, text: JSON.stringify({ ok: true, task_id, retried_to: task.to_name }) }],
|
|
1272
1278
|
};
|
|
@@ -1419,6 +1425,7 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
1419
1425
|
});
|
|
1420
1426
|
logTaskEvent(task_id, task.status, "delivered", from_session, `reassign: ${oldAlias} → ${reassignedAlias}`);
|
|
1421
1427
|
pushEvent(reassignedAlias, { type: "new_task", inbox_count: 1, priority: task.priority, from: from_session, ...(canonical.renamed ? { renamed_from: new_alias } : {}) }, effectiveNetId ?? task.network_id ?? null);
|
|
1428
|
+
pushNetworkObserverEvent(effectiveNetId ?? task.network_id ?? null, { type: "new_task", task_id, from: from_session, to: reassignedAlias, status: "delivered", priority: task.priority });
|
|
1422
1429
|
return { content: [{ type: "text" as const, text: JSON.stringify({ ok: true, task_id, reassigned_from: oldAlias, reassigned_to: reassignedAlias, ...(canonical.renamed ? { renamed_from: new_alias, renamed_to: reassignedAlias } : {}) }) }] };
|
|
1423
1430
|
}
|
|
1424
1431
|
);
|
package/src/uploads-http.test.ts
CHANGED
|
@@ -14,8 +14,11 @@ import { register, login } from "./auth.js";
|
|
|
14
14
|
|
|
15
15
|
const SERVER_DB = mkdtempSync(join(tmpdir(), "anet-upload-http-db-")) + "/commhub.db";
|
|
16
16
|
const UPLOADS_DIR = mkdtempSync(join(tmpdir(), "anet-upload-http-fs-"));
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
// #438 corrective: private bootServer({ port: 0 }) instance — the OS
|
|
18
|
+
// assigns the port and BASE is derived from the ACTUAL bound port, so
|
|
19
|
+
// this suite is immune to both the parent-side random-port TOCTOU and
|
|
20
|
+
// the aggregate module-cache no-op (import no longer boots anything).
|
|
21
|
+
let BASE = "";
|
|
19
22
|
|
|
20
23
|
let server: any;
|
|
21
24
|
let userToken = "";
|
|
@@ -27,7 +30,6 @@ beforeAll(async () => {
|
|
|
27
30
|
// we want to validate the production code path).
|
|
28
31
|
process.env.COMMHUB_DB = SERVER_DB;
|
|
29
32
|
process.env.COMMHUB_UPLOADS_DIR = UPLOADS_DIR;
|
|
30
|
-
process.env.PORT = String(PORT);
|
|
31
33
|
process.env.HOST = "127.0.0.1";
|
|
32
34
|
|
|
33
35
|
// Use a unique username + strong password each run so we don't trip
|
|
@@ -50,16 +52,17 @@ beforeAll(async () => {
|
|
|
50
52
|
}
|
|
51
53
|
expect(userToken).toBeTruthy();
|
|
52
54
|
|
|
53
|
-
//
|
|
54
|
-
// the port
|
|
55
|
-
await import("./
|
|
55
|
+
// Importing is side-effect-free now; boot a private instance and
|
|
56
|
+
// read the real port back.
|
|
57
|
+
const { bootServer } = await import("./server.js");
|
|
58
|
+
server = bootServer({ port: 0, hostname: "127.0.0.1" });
|
|
59
|
+
BASE = `http://127.0.0.1:${server.port}`;
|
|
56
60
|
// Tiny settle so the listener is ready.
|
|
57
61
|
await new Promise((r) => setTimeout(r, 100));
|
|
58
62
|
});
|
|
59
63
|
|
|
60
64
|
afterAll(() => {
|
|
61
|
-
|
|
62
|
-
// time so we can't easily stop it — process exit handles that.
|
|
65
|
+
try { server?.stop?.(true); } catch {}
|
|
63
66
|
try { rmSync(UPLOADS_DIR, { recursive: true, force: true }); } catch {}
|
|
64
67
|
try { rmSync(SERVER_DB, { recursive: true, force: true }); } catch {}
|
|
65
68
|
});
|