@sleep2agi/commhub-server 0.5.0-preview.35 → 0.5.0-preview.37
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 +1 -1
- package/src/auth.ts +8 -15
- package/src/tools.ts +13 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleep2agi/commhub-server",
|
|
3
|
-
"version": "0.5.0-preview.
|
|
3
|
+
"version": "0.5.0-preview.37",
|
|
4
4
|
"description": "CommHub Server \u2014 AI Agent communication hub with MCP protocol, multi-network isolation, user auth, and 18 MCP tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
package/src/auth.ts
CHANGED
|
@@ -85,22 +85,15 @@ export function login(username: string, password: string): AuthResult {
|
|
|
85
85
|
if (!user) return { ok: false, error: "invalid username or password" };
|
|
86
86
|
if (user.password_hash !== hashPassword(password)) return { ok: false, error: "invalid username or password" };
|
|
87
87
|
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
user.user_id);
|
|
92
|
-
|
|
88
|
+
// Issue a NEW user token — do NOT rotate/invalidate existing ones. Each
|
|
89
|
+
// login (cli, dashboard, second machine) gets its own row so they don't
|
|
90
|
+
// kick each other out of session. Tokens can be revoked via /api/auth/tokens.
|
|
93
91
|
const userToken = generateUserToken();
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
db.run(
|
|
100
|
-
"INSERT INTO api_tokens (token_id, token_hash, user_id, network_id, name, scope) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
|
101
|
-
[tokenId, hashToken(userToken), user.user_id, null, "user-login", "user"]
|
|
102
|
-
);
|
|
103
|
-
}
|
|
92
|
+
const tokenId = generateId("tok");
|
|
93
|
+
db.run(
|
|
94
|
+
"INSERT INTO api_tokens (token_id, token_hash, user_id, network_id, name, scope) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
|
95
|
+
[tokenId, hashToken(userToken), user.user_id, null, "user-login", "user"]
|
|
96
|
+
);
|
|
104
97
|
|
|
105
98
|
// Find default network
|
|
106
99
|
const defaultNet = db.get<any>(
|
package/src/tools.ts
CHANGED
|
@@ -428,12 +428,18 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
428
428
|
|
|
429
429
|
const session = scopedSessionStatus(alias, effectiveNetId);
|
|
430
430
|
|
|
431
|
-
// SSE push by alias
|
|
431
|
+
// SSE push by alias.
|
|
432
|
+
// The SSE channel is keyed by alias (subscribers connected to /events/<alias>),
|
|
433
|
+
// not by network_id. Earlier we gated the push on a network-scoped session
|
|
434
|
+
// lookup, which silently dropped pushes whenever an agent registered with
|
|
435
|
+
// network_id=null but the sender supplied an explicit network_id (the
|
|
436
|
+
// exact mismatch hit by Dashboard tasks). Push unconditionally; the
|
|
437
|
+
// subscriber's own auth (ntok_) constrains who can listen.
|
|
432
438
|
const pendingParams: any[] = [alias];
|
|
433
439
|
let pendingSql = "SELECT COUNT(*) as cnt FROM inbox WHERE session_name = ?1 AND acked = 0";
|
|
434
440
|
pendingSql = addScope(pendingSql, pendingParams, effectiveNetId);
|
|
435
441
|
const pending = db.get<{ cnt: number }>(pendingSql, ...pendingParams);
|
|
436
|
-
|
|
442
|
+
pushEvent(alias, { type: "new_task", inbox_count: pending?.cnt ?? 1, priority, from: from_session });
|
|
437
443
|
|
|
438
444
|
return {
|
|
439
445
|
content: [
|
|
@@ -471,7 +477,7 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
471
477
|
|
|
472
478
|
const session = scopedSessionStatus(alias, effectiveNetId);
|
|
473
479
|
|
|
474
|
-
|
|
480
|
+
pushEvent(alias, { type: "new_message", message, from: from_session, message_id: id });
|
|
475
481
|
|
|
476
482
|
return {
|
|
477
483
|
content: [
|
|
@@ -531,7 +537,7 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
531
537
|
if (replyLogged && in_reply_to) logTaskEvent(in_reply_to, null, replyStatus, from_session, text.slice(0, 200));
|
|
532
538
|
|
|
533
539
|
const session = scopedSessionStatus(alias, effectiveNetId);
|
|
534
|
-
|
|
540
|
+
pushEvent(alias, { type: "new_reply", from: from_session, message_id: id, in_reply_to, status: replyStatus });
|
|
535
541
|
|
|
536
542
|
return {
|
|
537
543
|
content: [{
|
|
@@ -607,10 +613,8 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
607
613
|
);
|
|
608
614
|
});
|
|
609
615
|
logTaskEvent(task_id, task.status, "delivered", from_session, "retry");
|
|
610
|
-
// SSE push
|
|
611
|
-
|
|
612
|
-
pushEvent(task.to_name, { type: "new_task", inbox_count: 1, priority: task.priority, from: from_session });
|
|
613
|
-
}
|
|
616
|
+
// SSE push (unconditional — channel is keyed by alias, not network)
|
|
617
|
+
pushEvent(task.to_name, { type: "new_task", inbox_count: 1, priority: task.priority, from: from_session });
|
|
614
618
|
return {
|
|
615
619
|
content: [{ type: "text" as const, text: JSON.stringify({ ok: true, task_id, retried_to: task.to_name }) }],
|
|
616
620
|
};
|
|
@@ -749,9 +753,7 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
749
753
|
[newInboxId, new_alias, task.priority, task.content, from_session, effectiveNetId ?? task.network_id ?? null]);
|
|
750
754
|
});
|
|
751
755
|
logTaskEvent(task_id, task.status, "delivered", from_session, `reassign: ${oldAlias} → ${new_alias}`);
|
|
752
|
-
|
|
753
|
-
pushEvent(new_alias, { type: "new_task", inbox_count: 1, priority: task.priority, from: from_session });
|
|
754
|
-
}
|
|
756
|
+
pushEvent(new_alias, { type: "new_task", inbox_count: 1, priority: task.priority, from: from_session });
|
|
755
757
|
return { content: [{ type: "text" as const, text: JSON.stringify({ ok: true, task_id, reassigned_from: oldAlias, reassigned_to: new_alias }) }] };
|
|
756
758
|
}
|
|
757
759
|
);
|