@sleep2agi/commhub-server 0.8.3 → 0.8.4-preview.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleep2agi/commhub-server",
3
- "version": "0.8.3",
3
+ "version": "0.8.4-preview.0",
4
4
  "description": "CommHub Server — AI Agent communication hub with MCP protocol, multi-network isolation, user auth, and 17 MCP tools.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/index.ts CHANGED
@@ -80,12 +80,12 @@ setInterval(() => {
80
80
  }, 300000);
81
81
 
82
82
  // ── Factory: 每个请求创建新的 McpServer(stateless 模式)──
83
- function createServer(clientIP?: string, enforceNetworkId?: string | null, enforceUserId?: string | null, callerAlias?: string | null, callerTokenIsNetwork = false): McpServer {
83
+ function createServer(clientIP?: string, enforceNetworkId?: string | null, enforceUserId?: string | null, callerAlias?: string | null, callerTokenIsNetwork = false, callerTokenId?: string | null): McpServer {
84
84
  const server = new McpServer({
85
85
  name: "commhub",
86
86
  version: "0.5.0",
87
87
  });
88
- registerTools(server, clientIP, enforceNetworkId, enforceUserId, callerAlias, callerTokenIsNetwork);
88
+ registerTools(server, clientIP, enforceNetworkId, enforceUserId, callerAlias, callerTokenIsNetwork, callerTokenId);
89
89
  return server;
90
90
  }
91
91
 
@@ -170,12 +170,12 @@ function requireTmuxAccess(req: Request, server?: any): Response | null {
170
170
  }
171
171
 
172
172
  // Extract user + network + token-binding identity from request token.
173
- function resolveRequestAuth(req: Request): { userId: string; networkId: string | null; username: string; tokenName: string | null } | null {
173
+ function resolveRequestAuth(req: Request): { userId: string; networkId: string | null; username: string; tokenName: string | null; tokenId: string | null } | null {
174
174
  const token = requestToken(req);
175
175
  if (!token) return null;
176
176
  const resolved = resolveToken(token);
177
177
  if (!resolved) return null;
178
- return { userId: resolved.user.user_id, networkId: resolved.networkId, username: resolved.user.username, tokenName: resolved.tokenName };
178
+ return { userId: resolved.user.user_id, networkId: resolved.networkId, username: resolved.user.username, tokenName: resolved.tokenName, tokenId: resolved.tokenId };
179
179
  }
180
180
 
181
181
  type RestNetworkScope = {
@@ -449,7 +449,7 @@ Bun.serve({
449
449
  const transport = new WebStandardStreamableHTTPServerTransport({
450
450
  sessionIdGenerator: undefined,
451
451
  });
452
- const mcpServer = createServer(clientIP, enforceNetId, authCtx?.userId || null, callerAlias, !!token?.startsWith("ntok_"));
452
+ const mcpServer = createServer(clientIP, enforceNetId, authCtx?.userId || null, callerAlias, !!token?.startsWith("ntok_"), authCtx?.tokenId || null);
453
453
  await mcpServer.connect(transport);
454
454
  const response = await transport.handleRequest(req);
455
455
  // Disconnect after response to prevent McpServer leak
package/src/tools.ts CHANGED
@@ -19,13 +19,13 @@ function normalizeMetaJson(meta: unknown): string | null {
19
19
  try { return JSON.stringify(meta); } catch { return null; }
20
20
  }
21
21
 
22
- export function registerTools(server: McpServer, clientIP?: string, enforceNetworkId?: string | null, enforceUserId?: string | null, callerAlias?: string | null, callerTokenIsNetwork = false) {
22
+ export function registerTools(server: McpServer, clientIP?: string, enforceNetworkId?: string | null, enforceUserId?: string | null, callerAlias?: string | null, callerTokenIsNetwork = false, callerTokenId?: string | null) {
23
23
  // Default from_session for outbound tools — extracted from the calling
24
24
  // token's binding (ntok_ → node alias, utok_ → username). Without this,
25
25
  // an agent's send_task call always claimed from='hub' and peer agents
26
- // couldn't tell who actually asked them. Tool callers can still override
27
- // by passing from_session explicitly.
28
- const defaultFrom = (clientFrom?: string) => clientFrom || callerAlias || "hub";
26
+ // couldn't tell who actually asked them. Network-bound node tokens are an
27
+ // identity boundary: they must not spoof another node via from_session.
28
+ const defaultFrom = (clientFrom?: string) => (callerTokenIsNetwork && callerAlias) ? callerAlias : (clientFrom || callerAlias || "hub");
29
29
  // If enforceNetworkId is set, override any client-supplied network_id
30
30
  const getNetworkId = (clientNetId?: string | null) => enforceNetworkId ?? clientNetId ?? null;
31
31
 
@@ -190,6 +190,11 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
190
190
  }
191
191
  }
192
192
  console.log(`[${ts()}] ${effectiveAlias} (${resume_id.slice(0, 8)}) → report_status: ${status}${task ? " | " + task.slice(0, 60) : ""}${effectiveNetId ? " [net]" : ""}${canonical.renamed ? ` [renamed from ${alias}]` : ""}`);
193
+ if (callerTokenIsNetwork && callerTokenId) {
194
+ try {
195
+ db.run("UPDATE api_tokens SET name = ?1 WHERE token_id = ?2", [`node:${effectiveAlias}`, callerTokenId]);
196
+ } catch {}
197
+ }
193
198
  const trimmedOutput = output?.slice(0, 4000);
194
199
  const hostHostname = host?.hostname || hn || null;
195
200
  const hostIp = host?.ip || clientIP || null;