@sleep2agi/commhub-server 0.9.0-preview.125 → 0.9.0-preview.126

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.9.0-preview.125",
3
+ "version": "0.9.0-preview.126",
4
4
  "description": "CommHub Server — AI Agent communication hub with MCP protocol, multi-network isolation, user auth, and MCP tools (17 collaboration-core + node/provider ops tools; authoritative list: docs-site/docs/api/mcp-tools.md).",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -0,0 +1,79 @@
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
2
+ import "./require-explicit-test-db.js";
3
+ import { db } from "./db.js";
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { registerTools } from "./tools.js";
6
+
7
+ // #1027: get_create_request must return network_id so the daemon can write
8
+ // it into the child config. Without it, child nodes fail the OpenCode
9
+ // runtime binding check and die within 5s.
10
+
11
+ const NET = "net_1027_hub";
12
+ const USER_ID = "u_1027_hub";
13
+ const DAEMON_ID = "node_1027_hub_daemon";
14
+ const DAEMON_ALIAS = "hub1027-daemon";
15
+ const DAEMON_TOK = "tok_1027_hub_daemon";
16
+
17
+ interface ToolHandler { (args: any, extra?: any): Promise<{ content: Array<{ type: "text"; text: string }> }>; }
18
+ interface Reply { ok?: boolean; error?: string; request_id?: string; network_id?: string; node_spec?: any; [k: string]: unknown; }
19
+
20
+ function cleanup() {
21
+ for (const t of ["node_create_requests", "audit_log", "nodes", "sessions", "api_tokens", "network_members"]) {
22
+ try { db.run(`DELETE FROM ${t} WHERE network_id = ?1`, [NET]); } catch {}
23
+ }
24
+ try { db.run("DELETE FROM networks WHERE network_id = ?1", [NET]); } catch {}
25
+ try { db.run("DELETE FROM users WHERE user_id = ?1", [USER_ID]); } catch {}
26
+ }
27
+ beforeEach(cleanup);
28
+ afterAll(cleanup);
29
+
30
+ function seed() {
31
+ db.run(`INSERT INTO users (user_id, username, password_hash, role, created_at) VALUES (?1, ?1, 'x', 'user', datetime('now'))`, [USER_ID]);
32
+ db.run(`INSERT OR REPLACE INTO networks (network_id, network_name, owner_id, created_at) VALUES (?1, ?1, ?2, datetime('now'))`, [NET, USER_ID]);
33
+ db.run(`INSERT INTO network_members (user_id, network_id, role, joined_at) VALUES (?1, ?2, 'admin', datetime('now'))`, [USER_ID, NET]);
34
+ db.run(
35
+ `INSERT INTO nodes (node_id, node_name, alias, network_id, config_snapshot, hostname, created_at, updated_at, lifecycle_state)
36
+ VALUES (?1, ?2, ?2, ?3, ?4, 'host-x', datetime('now'), datetime('now'), 'active')`,
37
+ [DAEMON_ID, DAEMON_ALIAS, NET, JSON.stringify({ role: "host_supervisor", daemon_capabilities: { runtimes_supported: ["opencode-cli"] } })],
38
+ );
39
+ db.run(
40
+ `INSERT INTO api_tokens (token_id, user_id, network_id, scope, name, token_hash, expires_at, revoked_at)
41
+ VALUES (?1, ?2, ?3, 'network', ?4, ?5, NULL, NULL)`,
42
+ [DAEMON_TOK, USER_ID, NET, `node:${DAEMON_ALIAS}`, `hash_${DAEMON_TOK}`],
43
+ );
44
+ }
45
+
46
+ function tools(callerTokenId: string | null = null): Record<string, ToolHandler> {
47
+ const server = new McpServer({ name: "test", version: "0" }) as any;
48
+ const out: Record<string, ToolHandler> = {};
49
+ const origTool = server.tool.bind(server);
50
+ server.tool = (name: string, d: string, schema: any, handler: ToolHandler) => { out[name] = handler; return origTool(name, d, schema, handler); };
51
+ const origRegisterTool = server.registerTool?.bind(server);
52
+ if (origRegisterTool) {
53
+ server.registerTool = (name: string, cfg: any, handler: ToolHandler) => { out[name] = handler; return origRegisterTool(name, cfg, handler); };
54
+ }
55
+ registerTools(server, undefined, NET, USER_ID, null, true, callerTokenId);
56
+ return out;
57
+ }
58
+
59
+ async function call(h: ToolHandler, args: any): Promise<Reply> {
60
+ return JSON.parse((await h(args)).content[0].text) as Reply;
61
+ }
62
+
63
+ describe("#1027 get_create_request returns network_id", () => {
64
+ test("network_id is included in the response", async () => {
65
+ seed();
66
+ const t = tools(DAEMON_TOK);
67
+ const r = await call(t.create_node, {
68
+ daemon_node_id: DAEMON_ID,
69
+ node_spec: { name: "child-1027", runtime: "opencode-cli", model: null, flags: {} },
70
+ });
71
+ expect(r.ok).toBe(true);
72
+ expect(r.request_id).toMatch(/^cr_/);
73
+
74
+ const g = await call(t.get_create_request, { request_id: r.request_id });
75
+ expect(g.ok).toBe(true);
76
+ expect(g.network_id).toBe(NET);
77
+ expect(g.node_spec?.name).toBe("child-1027");
78
+ });
79
+ });
package/src/tools.ts CHANGED
@@ -4952,6 +4952,8 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
4952
4952
  content: [{ type: "text" as const, text: JSON.stringify({
4953
4953
  ok: true,
4954
4954
  request_id,
4955
+ // #1027: daemon needs network_id to write into child config.
4956
+ network_id: row.network_id,
4955
4957
  node_spec: {
4956
4958
  name: specRow.child_name,
4957
4959
  runtime: specRow.runtime,