@datafrog-io/n2n-nexus 0.2.1 → 0.3.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/README.md CHANGED
@@ -10,77 +10,14 @@
10
10
 
11
11
  > **Works with:** Claude Code · Claude Desktop · VS Code · Cursor · Windsurf · Zed · JetBrains · Theia · Google Antigravity
12
12
 
13
- 📖 **Documentation:** [CHANGELOG](CHANGELOG.md) | [TODO](TODO.md) | [中文文档](docs/README_zh.md) | [AI Assistant Guide](docs/ASSISTANT_GUIDE.md)
14
-
15
- ## 🏛️ Architecture
16
-
17
- 1. **Nexus Room (Discussion)**: Unified public channel for all IDE assistants to coordinate across projects.
18
- 2. **Asset Vault (Archives)**:
19
- - **Manifest**: Technical details, billing, topology, and API specs for each project.
20
- - **Internal Docs**: Detailed technical implementation plans.
21
- - **Assets**: Local physical assets (Logos, UI screenshots, etc.).
22
- 3. **Global Knowledge**:
23
- - **Master Strategy**: Top-level strategic blueprint.
24
- - **Global Docs**: Cross-project common documents (e.g., Coding Standards, Roadmaps).
25
- 4. **Topology Engine**: Automated dependency graph analysis.
26
-
27
- ## 💾 Data Persistence
28
-
29
- Nexus stores all data in the local file system (customizable path), ensuring complete data sovereignty.
30
-
31
- **Directory Structure Example**:
32
- ```text
33
- Nexus_Storage/
34
- ├── global/
35
- │ ├── blueprint.md # Master Strategy
36
- │ ├── discussion.json # Global Chat History (fallback)
37
- │ ├── docs_index.json # Global Docs Index
38
- │ └── docs/ # Global Markdown Docs
39
- │ ├── coding-standards.md
40
- │ └── deployment-flow.md
41
- ├── projects/
42
- │ └── {project-id}/
43
- │ ├── manifest.json # Project Metadata
44
- │ ├── internal_blueprint.md # Technical Implementation Docs
45
- │ └── assets/ # Binary Assets (images, PDFs)
46
- ├── meetings/ # Meeting files (JSON fallback mode)
47
- │ └── {meeting-id}.json
48
- ├── registry.json # Global Project Index
49
- ├── archives/ # Reserved for backups
50
- └── nexus.db # SQLite Database (meetings, tasks, state)
51
- ```
52
-
53
- **Self-healing**: Core data files (e.g., `registry.json`, `discussion.json`) include automatic detection and repair mechanisms. If files are corrupted or missing, the system automatically rebuilds the initial state to ensure uninterrupted service.
54
-
55
- **Concurrency Safety**: All write operations to shared files (`discussion.json`, `registry.json`) are protected by an `AsyncMutex` lock, preventing race conditions when multiple AI agents communicate simultaneously.
56
-
57
- ## 🏷️ Project ID Conventions (Naming Standard)
58
-
59
- To ensure clarity and prevent collisions in the flat local namespace, all Project IDs MUST follow the **Prefix Dictionary** format: `[prefix]_[project-name]`.
13
+ 📖 **Documentation:** [CHANGELOG](CHANGELOG.md) | [TODO](TODO.md) | [中文文档](docs/README_zh.md) | [AI Assistant Guide](docs/ASSISTANT_GUIDE.md) | [Architecture](docs/ARCHITECTURE.md)
60
14
 
61
- | Prefix | Category | Example |
62
- | :--- | :--- | :--- |
63
- | `web_` | Websites, landing pages, domain-based projects | `web_datafrog.io` |
64
- | `api_` | Backend services, REST/gRPC APIs | `api_user-auth` |
65
- | `chrome_` | Chrome extensions | `chrome_evisa-helper` |
66
- | `vscode_` | VSCode extensions | `vscode_super-theme` |
67
- | `mcp_` | MCP Servers and MCP-related tools | `mcp_github-repo` |
68
- | `android_` | Native Android projects (Kotlin/Java) | `android_client-app` |
69
- | `ios_` | Native iOS projects (Swift/ObjC) | `ios_client-app` |
70
- | `flutter_` | **Mobile Cross-platform Special Case** | `flutter_unified-app` |
71
- | `desktop_` | General desktop apps (Tauri, Electron, etc.) | `desktop_main-hub` |
72
- | `lib_` | Shared libraries, SDKs, NPM/Python packages | `lib_crypto-core` |
73
- | `bot_` | Bots (Discord, Slack, DingTalk, etc.) | `bot_auto-moderator` |
74
- | `infra_` | Infrastructure as Code, CI/CD, DevOps scripts | `infra_k8s-config` |
75
- | `doc_` | Pure technical handbooks, strategies, roadmaps | `doc_coding-guide` |
76
-
77
- ---
78
15
 
79
16
  ## 🛠️ Toolset
80
17
 
81
18
  ### A. Session & Context
82
19
  - `register_session_context`: Declare the project ID currently active in the IDE to unlock write permissions.
83
- - `mcp://nexus/session`: View current identity, role (Moderator/Regular), and active project.
20
+ - `mcp://nexus/session`: View current identity, role (Host/Regular), and active project.
84
21
 
85
22
  ### B. Project Asset Management
86
23
  - `sync_project_assets`: **[Core/ASYNC]** Submit full Project Manifest and Internal Docs. Returns `taskId`.
@@ -101,8 +38,8 @@ To ensure clarity and prevent collisions in the flat local namespace, all Projec
101
38
  ### D. Meeting Management
102
39
  - `start_meeting`: Start a new tactical session for focused collaboration.
103
40
  - `reopen_meeting`: Reactivate a `closed` or `archived` session to continue discussion.
104
- - `end_meeting`: Conclude a meeting, lock history (**Moderator only**).
105
- - `archive_meeting`: Move closed meetings to cold storage (**Moderator only**).
41
+ - `end_meeting`: Conclude a meeting, lock history (**Host only**).
42
+ - `archive_meeting`: Move closed meetings to cold storage (**Host only**).
106
43
 
107
44
  ### E. Task Management (Phase 2 - ASYNC)
108
45
  - `create_task`: Create a new background task. Link to meeting for traceability.
@@ -111,9 +48,9 @@ To ensure clarity and prevent collisions in the flat local namespace, all Projec
111
48
  - `update_task`: Update progress or result (typically for workers).
112
49
  - `cancel_task`: Cancel a pending or running task.
113
50
 
114
- ### F. Admin (Moderator Only)
115
- - `moderator_maintenance`: Prune or clear system logs.
116
- - `moderator_delete_project`: Completely remove a project and its assets.
51
+ ### F. Host (Host Only)
52
+ - `host_maintenance`: Prune or clear system logs.
53
+ - `host_delete_project`: Completely remove a project and its assets.
117
54
 
118
55
  ## 📄 Resources (URI)
119
56
 
@@ -133,13 +70,39 @@ To ensure clarity and prevent collisions in the flat local namespace, all Projec
133
70
  - `mcp://nexus/docs/{docId}`: Read a specific shared document.
134
71
  - `mcp://nexus/meetings/{meetingId}`: Full transcript for a specific meeting.
135
72
 
73
+ ## 🌐 Global Hub Architecture
74
+
75
+ **v0.3.0** introduces a fully automatic, zero-configuration collaboration architecture:
76
+
77
+ ```
78
+ ┌─────────────────────────────────────────────────────────────┐
79
+ │ Global Nexus Hub │
80
+ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
81
+ │ │ Cursor │ │ VS Code │ │ Claude │ │ Zed │ │
82
+ │ │ (Guest) │ │ (Guest) │ │ (Host) │ │ (Guest) │ │
83
+ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
84
+ │ │ │ │ │ │
85
+ │ └─────────────┴──────┬──────┴─────────────┘ │
86
+ │ │ SSE │
87
+ │ ┌───────▼───────┐ │
88
+ │ │ Port 5688 │ │
89
+ │ │ (Auto-Elected)│ │
90
+ │ └───────────────┘ │
91
+ └─────────────────────────────────────────────────────────────┘
92
+ ```
93
+
94
+ - **Zero Config**: Just run `npx @datafrog-io/n2n-nexus` - no `--id` or `--host` required.
95
+ - **Auto Election**: First instance binds port 5688 and becomes Host; others join as Guests.
96
+ - **Cross-Project Sync**: All IDEs share the same Hub, enabling real-time cross-project meetings.
97
+ - **Hot Failover**: If Host disconnects, a Guest automatically promotes within 10 seconds.
98
+
136
99
  ## 🚀 Quick Start
137
100
 
138
101
  ### MCP Configuration (Recommended)
139
102
 
140
103
  Add to your MCP config file (e.g., `claude_desktop_config.json` or Cursor MCP settings):
141
104
 
142
- #### Moderator (Admin AI)
105
+ #### Leader AI
143
106
  ```json
144
107
  {
145
108
  "mcpServers": {
@@ -148,8 +111,6 @@ Add to your MCP config file (e.g., `claude_desktop_config.json` or Cursor MCP se
148
111
  "args": [
149
112
  "-y",
150
113
  "@datafrog-io/n2n-nexus",
151
- "--id", "Master-AI",
152
- "--moderator",
153
114
  "--root", "D:/DevSpace/Nexus_Storage"
154
115
  ]
155
116
  }
@@ -157,7 +118,7 @@ Add to your MCP config file (e.g., `claude_desktop_config.json` or Cursor MCP se
157
118
  }
158
119
  ```
159
120
 
160
- #### Regular AI
121
+ #### Collaborator AI
161
122
  ```json
162
123
  {
163
124
  "mcpServers": {
@@ -166,7 +127,6 @@ Add to your MCP config file (e.g., `claude_desktop_config.json` or Cursor MCP se
166
127
  "args": [
167
128
  "-y",
168
129
  "@datafrog-io/n2n-nexus",
169
- "--id", "Assistant-AI",
170
130
  "--root", "D:/DevSpace/Nexus_Storage"
171
131
  ]
172
132
  }
@@ -177,11 +137,9 @@ Add to your MCP config file (e.g., `claude_desktop_config.json` or Cursor MCP se
177
137
  ### CLI Arguments
178
138
  | Argument | Description | Default |
179
139
  |----------|-------------|---------|
180
- | `--id` | Instance identifier for this AI agent | `Assistant` |
181
- | `--moderator` | Grant admin privileges to this instance | `false` |
182
140
  | `--root` | Local storage path for all Nexus data | `./storage` |
183
141
 
184
- > **Note:** Only instances with `--moderator` flag can use admin tools (e.g., `moderator_maintenance`).
142
+ > **Note:** Host identity and Instance ID are determined automatically based on the project folder name and startup order.
185
143
 
186
144
  ### Local Development
187
145
  ```bash
@@ -189,7 +147,7 @@ git clone https://github.com/n2ns/n2n-nexus.git
189
147
  cd n2n-nexus
190
148
  npm install
191
149
  npm run build
192
- npm start -- --id Master-AI --root ./my-storage
150
+ npm start -- --root ./my-storage
193
151
  ```
194
152
 
195
153
  ---
@@ -213,4 +171,13 @@ The following files demonstrate a real orchestration session where **4 AI agents
213
171
  > *This is what AI-native development looks like.*
214
172
 
215
173
  ---
216
- © 2025 datafrog.io. Built for Local-Only AI Workflows.
174
+
175
+ ## ⭐ Support This Project
176
+
177
+ If **n2ns Nexus** helps you build better AI workflows, consider giving it a star! Your support helps us improve and motivates continued development.
178
+
179
+ [![Star on GitHub](https://img.shields.io/github/stars/n2ns/n2n-nexus?style=social)](https://github.com/n2ns/n2n-nexus)
180
+
181
+ ---
182
+
183
+ © 2026 datafrog.io. Built for Local-Only AI Workflows.
package/build/config.js CHANGED
@@ -1,14 +1,186 @@
1
1
  import path from "path";
2
2
  import { fileURLToPath } from "url";
3
+ import os from "os";
4
+ import fs from "fs";
5
+ import http from "http";
3
6
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
4
7
  const args = process.argv.slice(2);
8
+ // Load version from package.json
9
+ const pkgPath = path.resolve(__dirname, "../package.json");
10
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
5
11
  const getArg = (k) => {
6
12
  const i = args.indexOf(k);
7
13
  return i !== -1 && args[i + 1] ? args[i + 1] : "";
8
14
  };
9
- const hasFlag = (k) => args.includes(k);
15
+ const hasFlag = (k) => args.includes(k) || args.includes(k.charAt(1) === "-" ? k : k.substring(0, 2));
16
+ // --- CLI Commands Handlers ---
17
+ if (hasFlag("--help") || hasFlag("-h")) {
18
+ console.log(`
19
+ n2ns Nexus 🚀 - Local Digital Asset Hub (MCP Server) v${pkg.version}
20
+
21
+ USAGE:
22
+ npx -y @datafrog-io/n2n-nexus [options]
23
+
24
+ DESCRIPTION:
25
+ A local-first project management and collaboration hub designed for
26
+ multi-AI assistant coordination across different IDEs (Cursor, VS Code, etc.).
27
+
28
+ OPTIONS:
29
+ --root <path> Directory for data persistence. Default: ./storage
30
+ --version, -v Show version number.
31
+ --help, -h Show this message.
32
+
33
+ MCP CONFIG EXAMPLE (claude_desktop_config.json):
34
+ {
35
+ "mcpServers": {
36
+ "n2n-nexus": {
37
+ "command": "npx",
38
+ "args": ["-y", "@datafrog-io/n2n-nexus", "--root", "/path/to/storage"]
39
+ }
40
+ }
41
+ }
42
+
43
+ ENVIRONMENT VARIABLES:
44
+ NEXUS_ROOT Override default storage path.
45
+ `);
46
+ process.exit(0);
47
+ }
48
+ if (hasFlag("--version") || hasFlag("-v")) {
49
+ console.log(pkg.version);
50
+ process.exit(0);
51
+ }
52
+ // --- Path Normalization Logic ---
53
+ function normalizeRootPath(inputPath) {
54
+ // 1. Priority: CLI --root > ENV NEXUS_ROOT > Default ./storage
55
+ let root = inputPath || process.env.NEXUS_ROOT || path.join(__dirname, "../storage");
56
+ // 2. Resolve ~ to home directory
57
+ if (root.startsWith("~")) {
58
+ root = path.join(os.homedir(), root.slice(1));
59
+ }
60
+ // 3. Cross-platform adaptation (WSL <-> Windows)
61
+ // If running on Linux (WSL) but path looks like Windows (D:/ or C:\\)
62
+ if (process.platform === "linux" && /^[a-zA-Z]:[/\\]/.test(root)) {
63
+ const drive = root[0].toLowerCase();
64
+ root = `/mnt/${drive}${root.slice(2).replace(/\\/g, "/")}`;
65
+ }
66
+ return path.resolve(root);
67
+ }
68
+ /**
69
+ * Probe a port to see if it's a Nexus Host
70
+ */
71
+ async function probeHost(port) {
72
+ return new Promise((resolve) => {
73
+ const req = http.get(`http://127.0.0.1:${port}/hello`, { timeout: 500 }, (res) => {
74
+ let data = "";
75
+ res.on("data", (chunk) => data += chunk);
76
+ res.on("end", () => {
77
+ try {
78
+ const info = JSON.parse(data);
79
+ if (info.service === "n2n-nexus" && info.role === "host") {
80
+ resolve({ isNexus: true, rootStorage: info.rootStorage });
81
+ }
82
+ else {
83
+ resolve({ isNexus: false });
84
+ }
85
+ }
86
+ catch {
87
+ resolve({ isNexus: false });
88
+ }
89
+ });
90
+ });
91
+ req.on("error", () => resolve({ isNexus: false }));
92
+ req.on("timeout", () => {
93
+ req.destroy();
94
+ resolve({ isNexus: false });
95
+ });
96
+ });
97
+ }
98
+ /**
99
+ * Automatic Host Election (Port-Based 5688-5700)
100
+ * Strategy: Probe-First + Atomic Bind + Join Winner on Failure
101
+ *
102
+ * 1. First, scan all ports to find existing Host
103
+ * 2. If found, join it immediately
104
+ * 3. If not found, try to become Host
105
+ * 4. If bind fails, wait and re-probe (give winner time to start)
106
+ */
107
+ async function isHostAutoElection(root) {
108
+ const startPort = 5688;
109
+ const endPort = 5700;
110
+ // Phase 1: Probe-First - Check if any Host already exists
111
+ for (let port = startPort; port <= endPort; port++) {
112
+ const probe = await probeHost(port);
113
+ if (probe.isNexus) {
114
+ return { isHost: false, port, rootStorage: probe.rootStorage };
115
+ }
116
+ }
117
+ // Phase 2: No Host found, attempt to become Host
118
+ for (let port = startPort; port <= endPort; port++) {
119
+ const result = await new Promise((resolve) => {
120
+ const server = http.createServer((req, res) => {
121
+ if (req.url === "/hello") {
122
+ res.writeHead(200, { "Content-Type": "application/json" });
123
+ res.end(JSON.stringify({
124
+ service: "n2n-nexus",
125
+ role: "host",
126
+ version: pkg.version,
127
+ rootStorage: root
128
+ }));
129
+ return;
130
+ }
131
+ res.writeHead(404);
132
+ res.end();
133
+ });
134
+ server.on("error", (err) => {
135
+ if (err.code === "EADDRINUSE") {
136
+ resolve({ isHost: false });
137
+ }
138
+ else {
139
+ resolve({ isHost: false });
140
+ }
141
+ });
142
+ server.listen(port, "127.0.0.1", () => {
143
+ resolve({ isHost: true, server });
144
+ });
145
+ });
146
+ if (result.isHost) {
147
+ return { isHost: true, port, server: result.server };
148
+ }
149
+ // Phase 3: Bind failed - another Guest won. Wait then join winner.
150
+ await new Promise(r => setTimeout(r, 10000)); // Give winner 10s to start /hello
151
+ const probe = await probeHost(port);
152
+ if (probe.isNexus) {
153
+ return { isHost: false, port, rootStorage: probe.rootStorage };
154
+ }
155
+ // If still not Nexus, try next port (occupied by non-Nexus service)
156
+ }
157
+ // Fallback: become Host on startPort (should rarely happen)
158
+ return { isHost: true, port: startPort };
159
+ }
160
+ /**
161
+ * Automatic Project Name Detection
162
+ */
163
+ function getAutoProjectName() {
164
+ try {
165
+ const pkgPath = path.join(process.cwd(), "package.json");
166
+ if (fs.existsSync(pkgPath)) {
167
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
168
+ if (pkg.name)
169
+ return pkg.name.split("/").pop() || pkg.name;
170
+ }
171
+ }
172
+ catch { /* ignore */ }
173
+ return path.basename(process.cwd()) || "Assistant";
174
+ }
175
+ const rootPath = normalizeRootPath(getArg("--root"));
176
+ const election = await isHostAutoElection(rootPath);
177
+ const projectName = getAutoProjectName();
178
+ export const hostServer = election.server;
10
179
  export const CONFIG = {
11
- instanceId: getArg("--id") || "Assistant",
12
- isModerator: hasFlag("--moderator"),
13
- rootStorage: path.resolve(getArg("--root") || path.join(__dirname, "../storage"))
180
+ // Priority: CLI --id > Auto-named (Project Name only)
181
+ instanceId: getArg("--id") || projectName,
182
+ isHost: election.isHost,
183
+ // Inherit storage path if Guest, otherwise use local resolved path
184
+ rootStorage: election.isHost ? rootPath : (election.rootStorage || rootPath),
185
+ port: election.port
14
186
  };
package/build/index.js CHANGED
@@ -2,15 +2,17 @@
2
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js";
5
+ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
5
6
  import { readFileSync } from "fs";
6
7
  import { join } from "path";
7
8
  import { fileURLToPath } from "url";
8
- import { CONFIG } from "./config.js";
9
+ import http from "http";
10
+ import { CONFIG, hostServer } from "./config.js";
9
11
  import { StorageManager } from "./storage/index.js";
10
12
  import { TOOL_DEFINITIONS, handleToolCall } from "./tools/index.js";
11
13
  import { listResources, getResourceContent } from "./resources/index.js";
12
14
  import { sanitizeErrorMessage } from "./utils/error.js";
13
- import { checkModeratorPermission } from "./utils/auth.js";
15
+ import { checkHostPermission } from "./utils/auth.js";
14
16
  const __dirname = fileURLToPath(new URL(".", import.meta.url));
15
17
  const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
16
18
  /**
@@ -21,6 +23,7 @@ const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8")
21
23
  class NexusServer {
22
24
  server;
23
25
  currentProject = null;
26
+ sseTransports = new Map();
24
27
  constructor() {
25
28
  this.server = new Server({ name: "n2n-nexus", version: pkg.version }, { capabilities: { resources: {}, tools: {}, prompts: {} } });
26
29
  this.setupHandlers();
@@ -62,8 +65,8 @@ class NexusServer {
62
65
  this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
63
66
  const { name, arguments: toolArgs } = request.params;
64
67
  try {
65
- if (name.startsWith("moderator_"))
66
- checkModeratorPermission(name);
68
+ if (name.startsWith("host_"))
69
+ checkHostPermission(name);
67
70
  const result = await handleToolCall(name, toolArgs, {
68
71
  currentProject: this.currentProject,
69
72
  setCurrentProject: (id) => { this.currentProject = id; },
@@ -131,29 +134,143 @@ class NexusServer {
131
134
  const shutdown = async (signal) => {
132
135
  console.error(`\n[Nexus] Received ${signal}. Shutting down...`);
133
136
  try {
134
- // Post-departure log
135
137
  const msg = `Nexus Session Terminated (IDE Closed).`;
136
138
  await StorageManager.addGlobalLog(`SYSTEM:${CONFIG.instanceId}`, msg, "UPDATE");
137
139
  console.error(`[Nexus:${CONFIG.instanceId}] Goodbye!`);
138
140
  }
139
- catch {
140
- // Ignore if storage is already cleaned up
141
- }
141
+ catch { /* ignore */ }
142
142
  process.exit(0);
143
143
  };
144
144
  process.on("SIGINT", () => shutdown("SIGINT"));
145
145
  process.on("SIGTERM", () => shutdown("SIGTERM"));
146
- const transport = new StdioServerTransport();
147
- await this.server.connect(transport);
148
- // Announce presence
149
- try {
146
+ if (CONFIG.isHost && hostServer) {
147
+ // --- HOST MODE: Central Hub ---
150
148
  await StorageManager.init();
151
- const onlineMsg = `Nexus Session Active (IDE Opened). Role: ${CONFIG.isModerator ? "Moderator" : "Regular"}`;
149
+ hostServer.on("request", async (req, res) => {
150
+ const url = new URL(req.url || "", `http://${req.headers.host}`);
151
+ if (url.pathname === "/mcp") {
152
+ const guestId = url.searchParams.get("id") || "UnknownGuest";
153
+ if (req.method === "GET") {
154
+ const transport = new SSEServerTransport("/mcp", res);
155
+ this.sseTransports.set(transport.sessionId, transport);
156
+ const msg = `Guest Joined: ${guestId}`;
157
+ await StorageManager.addGlobalLog(`HOST:${CONFIG.instanceId}`, msg, "UPDATE");
158
+ console.error(`[Nexus Hub] ${msg} (Session: ${transport.sessionId})`);
159
+ // Heartbeat: keep connection alive
160
+ const heartbeat = setInterval(() => {
161
+ try {
162
+ res.write(": ping\n\n");
163
+ }
164
+ catch {
165
+ clearInterval(heartbeat);
166
+ }
167
+ }, 30000);
168
+ transport.onclose = () => {
169
+ this.sseTransports.delete(transport.sessionId);
170
+ clearInterval(heartbeat);
171
+ console.error(`[Nexus Hub] Guest Left: ${guestId}`);
172
+ };
173
+ await this.server.connect(transport);
174
+ return;
175
+ }
176
+ else if (req.method === "POST") {
177
+ const sessionId = url.searchParams.get("sessionId");
178
+ const transport = sessionId ? this.sseTransports.get(sessionId) : null;
179
+ if (transport) {
180
+ await transport.handlePostMessage(req, res);
181
+ }
182
+ else {
183
+ res.writeHead(404).end("Session unknown");
184
+ }
185
+ return;
186
+ }
187
+ }
188
+ });
189
+ // Support local stdio for the host's own IDE
190
+ const transport = new StdioServerTransport();
191
+ await this.server.connect(transport);
192
+ const onlineMsg = `Nexus Hub Active. Playing Host.`;
152
193
  await StorageManager.addGlobalLog(`SYSTEM:${CONFIG.instanceId}`, onlineMsg, "UPDATE");
153
- console.error(`[Nexus:${CONFIG.instanceId}] ${onlineMsg}`);
194
+ console.error(`[Nexus:${CONFIG.instanceId}] ${onlineMsg} (Port: ${CONFIG.port})`);
154
195
  }
155
- catch (e) {
156
- console.error("[Nexus] Failed to post online message:", e);
196
+ else {
197
+ // --- GUEST MODE: SSE Proxy ---
198
+ const guestId = CONFIG.instanceId;
199
+ // Random delay function to prevent thundering herd during re-election
200
+ const randomDelay = () => Math.floor(Math.random() * 3000);
201
+ const startProxy = () => {
202
+ // Clear any stale stdin listeners before starting
203
+ process.stdin.removeAllListeners("data");
204
+ console.error(`[Nexus:${guestId}] Global Hub detected at ${CONFIG.port}. Joining...`);
205
+ let sessionId = null;
206
+ let lastActivity = Date.now();
207
+ // Watchdog: trigger re-election if Host is silent for too long
208
+ const watchdog = setInterval(() => {
209
+ if (Date.now() - lastActivity > 60000) {
210
+ console.error("[Nexus Guest] Host stale. Triggering re-election...");
211
+ cleanup();
212
+ // Random delay to prevent all guests from racing for the port
213
+ setTimeout(() => this.run(), randomDelay());
214
+ }
215
+ }, 10000);
216
+ const cleanup = () => {
217
+ clearInterval(watchdog);
218
+ process.stdin.removeAllListeners("data");
219
+ };
220
+ const stdioHandler = (chunk) => {
221
+ if (!sessionId)
222
+ return;
223
+ try {
224
+ const req = http.request({
225
+ hostname: "127.0.0.1",
226
+ port: CONFIG.port,
227
+ path: `/mcp?sessionId=${sessionId}&id=${guestId}`,
228
+ method: "POST",
229
+ headers: { "Content-Type": "application/json" }
230
+ });
231
+ // Handle request errors to prevent unhandled exceptions
232
+ req.on("error", () => { });
233
+ req.write(chunk);
234
+ req.end();
235
+ }
236
+ catch { /* suppress */ }
237
+ };
238
+ process.stdin.on("data", stdioHandler);
239
+ http.get(`http://127.0.0.1:${CONFIG.port}/mcp?id=${guestId}`, (res) => {
240
+ let buffer = "";
241
+ res.on("data", (chunk) => {
242
+ lastActivity = Date.now();
243
+ const str = chunk.toString();
244
+ buffer += str;
245
+ if (!sessionId && buffer.includes("event: endpoint")) {
246
+ const match = buffer.match(/sessionId=([a-f0-9-]+)/);
247
+ if (match)
248
+ sessionId = match[1];
249
+ }
250
+ if (str.includes("event: message")) {
251
+ const lines = str.split("\n");
252
+ const dataLine = lines.find((l) => l.startsWith("data: "));
253
+ if (dataLine) {
254
+ try {
255
+ process.stdout.write(dataLine.substring(6) + "\n");
256
+ }
257
+ catch { }
258
+ }
259
+ }
260
+ });
261
+ res.on("end", () => {
262
+ console.error("[Nexus Guest] Lost connection to Host. Re-electing...");
263
+ cleanup();
264
+ // Random delay for re-election
265
+ setTimeout(() => this.run(), randomDelay());
266
+ });
267
+ }).on("error", () => {
268
+ console.error("[Nexus Guest] Proxy Receive Error. Retry with random delay...");
269
+ cleanup();
270
+ setTimeout(() => this.run(), 1000 + randomDelay());
271
+ });
272
+ };
273
+ startProxy();
157
274
  }
158
275
  }
159
276
  }
@@ -22,8 +22,8 @@ export async function getResourceContent(uri, currentProject) {
22
22
  if (uri === "mcp://nexus/session") {
23
23
  const info = {
24
24
  yourId: CONFIG.instanceId,
25
- role: CONFIG.isModerator ? "Moderator" : "Regular",
26
- isModerator: CONFIG.isModerator,
25
+ role: CONFIG.isHost ? "Host" : "Regular",
26
+ isHost: CONFIG.isHost,
27
27
  activeProject: currentProject || "None"
28
28
  };
29
29
  return { mimeType: "application/json", text: JSON.stringify(info, null, 2) };
@@ -166,7 +166,7 @@ const ALL_TOOLS = [
166
166
  },
167
167
  {
168
168
  name: "end_meeting",
169
- description: "[Moderator] End active meeting. Locks history.",
169
+ description: "[Host] End active meeting. Locks history.",
170
170
  inputSchema: {
171
171
  type: "object",
172
172
  properties: {
@@ -177,7 +177,7 @@ const ALL_TOOLS = [
177
177
  },
178
178
  {
179
179
  name: "archive_meeting",
180
- description: "[Moderator] Archive closed meeting. Read-only after.",
180
+ description: "[Host] Archive closed meeting. Read-only after.",
181
181
  inputSchema: {
182
182
  type: "object",
183
183
  properties: {
@@ -258,10 +258,10 @@ const ALL_TOOLS = [
258
258
  required: ["taskId"]
259
259
  }
260
260
  },
261
- // --- Admin (Moderator Only) ---
261
+ // --- Host (Host Only) ---
262
262
  {
263
- name: "moderator_maintenance",
264
- description: "[Moderator] Manage logs: 'prune' oldest N or 'clear' all.",
263
+ name: "host_maintenance",
264
+ description: "[Host] Manage logs: 'prune' oldest N or 'clear' all.",
265
265
  inputSchema: {
266
266
  type: "object",
267
267
  properties: {
@@ -272,8 +272,8 @@ const ALL_TOOLS = [
272
272
  }
273
273
  },
274
274
  {
275
- name: "moderator_delete_project",
276
- description: "[ASYNC][Moderator] Delete project. Irreversible. Returns taskId.",
275
+ name: "host_delete_project",
276
+ description: "[ASYNC][Host] Delete project. Irreversible. Returns taskId.",
277
277
  inputSchema: {
278
278
  type: "object",
279
279
  properties: {
@@ -45,10 +45,10 @@ export async function handleToolCall(name, toolArgs, ctx) {
45
45
  return handleUpdateProject(validatedArgs);
46
46
  case "rename_project":
47
47
  return handleRenameProject(validatedArgs, ctx);
48
- case "moderator_delete_project":
48
+ case "host_delete_project":
49
49
  return handleRemoveProject(validatedArgs, ctx);
50
- case "moderator_maintenance":
51
- return handleModeratorMaintenance(validatedArgs, ctx);
50
+ case "host_maintenance":
51
+ return handleHostMaintenance(validatedArgs, ctx);
52
52
  // --- Meeting Tools ---
53
53
  case "start_meeting":
54
54
  return handleStartMeeting(validatedArgs, ctx);
@@ -321,12 +321,12 @@ async function handleUpdateStrategy(args, _ctx) {
321
321
  return { content: [{ type: "text", text: "Strategy updated." }] };
322
322
  }
323
323
  /**
324
- * ASYNC: moderator_delete_project now returns a taskId.
324
+ * ASYNC: host_delete_project now returns a taskId.
325
325
  */
326
326
  async function handleRemoveProject(args, ctx) {
327
- // Defense-in-Depth: Explicit moderator check
328
- if (!CONFIG.isModerator) {
329
- throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Only moderators can delete projects.");
327
+ // Defense-in-Depth: Explicit host check
328
+ if (!CONFIG.isHost) {
329
+ throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Only hosts can delete projects.");
330
330
  }
331
331
  if (!args?.projectId)
332
332
  throw new McpError(ErrorCode.InvalidParams, "projectId is required.");
@@ -337,7 +337,7 @@ async function handleRemoveProject(args, ctx) {
337
337
  // Create background task
338
338
  const task = createTask({
339
339
  metadata: {
340
- operation: "moderator_delete_project",
340
+ operation: "host_delete_project",
341
341
  projectId: args.projectId,
342
342
  initiator: CONFIG.instanceId
343
343
  }
@@ -351,7 +351,7 @@ async function handleRemoveProject(args, ctx) {
351
351
  ctx.notifyResourceUpdate("mcp://nexus/hub/registry");
352
352
  ctx.notifyResourceUpdate("mcp://nexus/get_global_topology");
353
353
  updateTask(task.id, { status: "completed", progress: 1.0 });
354
- await StorageManager.addGlobalLog("SYSTEM", `[${CONFIG.instanceId}] Task Completed: Project '${args.projectId}' deleted by moderator.`);
354
+ await StorageManager.addGlobalLog("SYSTEM", `[${CONFIG.instanceId}] Task Completed: Project '${args.projectId}' deleted by host.`);
355
355
  }
356
356
  catch (error) {
357
357
  updateTask(task.id, {
@@ -380,10 +380,10 @@ async function handleSyncGlobalDoc(args) {
380
380
  return { content: [{ type: "text", text: `Global document '${args.docId}' synchronized.` }] };
381
381
  }
382
382
  // --- Admin Handlers ---
383
- async function handleModeratorMaintenance(args, ctx) {
384
- // Defense-in-Depth: Explicit moderator check
385
- if (!CONFIG.isModerator) {
386
- throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Only moderators can perform maintenance.");
383
+ async function handleHostMaintenance(args, ctx) {
384
+ // Defense-in-Depth: Explicit host check
385
+ if (!CONFIG.isHost) {
386
+ throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Only hosts can perform maintenance.");
387
387
  }
388
388
  if (!args.action || args.count === undefined) {
389
389
  throw new McpError(ErrorCode.InvalidParams, "Both 'action' and 'count' are mandatory for maintenance.");
@@ -434,9 +434,9 @@ async function handleEndMeeting(args, ctx) {
434
434
  throw new McpError(ErrorCode.InvalidRequest, "No active meeting found to end. Please specify meetingId.");
435
435
  targetId = active.id;
436
436
  }
437
- // STRICT: Only moderators can end meetings
438
- if (!CONFIG.isModerator) {
439
- throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Only moderators can end meetings.");
437
+ // STRICT: Only hosts can end meetings
438
+ if (!CONFIG.isHost) {
439
+ throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Only hosts can end meetings.");
440
440
  }
441
441
  const { meeting, suggestedSyncTargets } = await UnifiedMeetingStore.endMeeting(targetId, args.summary, undefined);
442
442
  ctx.notifyResourceUpdate("mcp://nexus/status");
@@ -458,9 +458,9 @@ async function handleEndMeeting(args, ctx) {
458
458
  async function handleArchiveMeeting(args, _ctx) {
459
459
  if (!args.meetingId)
460
460
  throw new McpError(ErrorCode.InvalidParams, "meetingId is required.");
461
- // STRICT: Only moderators can archive meetings
462
- if (!CONFIG.isModerator) {
463
- throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Only moderators can archive meetings.");
461
+ // STRICT: Only hosts can archive meetings
462
+ if (!CONFIG.isHost) {
463
+ throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Only hosts can archive meetings.");
464
464
  }
465
465
  await UnifiedMeetingStore.archiveMeeting(args.meetingId, undefined);
466
466
  return {
@@ -111,16 +111,16 @@ export const RenameProjectSchema = z.object({
111
111
  newId: ProjectIdSchema
112
112
  });
113
113
  /**
114
- * 15. moderator_maintenance
114
+ * 15. host_maintenance
115
115
  */
116
- export const ModeratorMaintenanceSchema = z.object({
116
+ export const HostMaintenanceSchema = z.object({
117
117
  action: z.enum(["prune", "clear"]),
118
118
  count: z.number().int().min(0)
119
119
  });
120
120
  /**
121
- * 16. moderator_delete_project
121
+ * 16. host_delete_project
122
122
  */
123
- export const ModeratorDeleteSchema = z.object({
123
+ export const HostDeleteSchema = z.object({
124
124
  projectId: ProjectIdSchema
125
125
  });
126
126
  /**
@@ -230,24 +230,24 @@ export const TOOL_REGISTRY = {
230
230
  description: "[ASYNC] Rename a project ID with automatic cascading updates to all relation references. Returns task ID.",
231
231
  schema: RenameProjectSchema
232
232
  },
233
- moderator_maintenance: {
234
- description: "[ADMIN ONLY] Manage global discussion logs. 'prune' removes the oldest N entries (keeps newest). 'clear' wipes all logs (use count=0). Returns summary of removed entries. Irreversible.",
235
- schema: ModeratorMaintenanceSchema
233
+ host_maintenance: {
234
+ description: "[HOST ONLY] Manage global discussion logs. 'prune' removes the oldest N entries (keeps newest). 'clear' wipes all logs (use count=0). Returns summary of removed entries. Irreversible.",
235
+ schema: HostMaintenanceSchema
236
236
  },
237
- moderator_delete_project: {
238
- description: "[ASYNC][ADMIN ONLY] Completely remove a project, its manifest, and all its assets from Nexus Hub. Returns task ID. Irreversible.",
239
- schema: ModeratorDeleteSchema
237
+ host_delete_project: {
238
+ description: "[ASYNC][HOST ONLY] Completely remove a project, its manifest, and all its assets from Nexus Hub. Returns task ID. Irreversible.",
239
+ schema: HostDeleteSchema
240
240
  },
241
241
  start_meeting: {
242
242
  description: "Start a new meeting session. Creates a dedicated file for the meeting. Returns the meeting ID and details.",
243
243
  schema: StartMeetingSchema
244
244
  },
245
245
  end_meeting: {
246
- description: "End an active meeting. Locks the session for further messages. [RESTRICTED: Only moderator can end].",
246
+ description: "End an active meeting. Locks the session for further messages. [RESTRICTED: Only host can end].",
247
247
  schema: EndMeetingSchema
248
248
  },
249
249
  archive_meeting: {
250
- description: "Archive a closed meeting. Archived meetings are read-only and excluded from active queries. [RESTRICTED: Only moderator can archive].",
250
+ description: "Archive a closed meeting. Archived meetings are read-only and excluded from active queries. [RESTRICTED: Only host can archive].",
251
251
  schema: ArchiveMeetingSchema
252
252
  },
253
253
  reopen_meeting: {
@@ -1,11 +1,11 @@
1
1
  import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
2
2
  import { CONFIG } from "../config.js";
3
3
  /**
4
- * Validates moderator permissions for admin tools.
5
- * @throws McpError if current session is not in Moderator mode.
4
+ * Validates host permissions for privileged tools.
5
+ * @throws McpError if current session is not in Host mode.
6
6
  */
7
- export function checkModeratorPermission(toolName) {
8
- if (!CONFIG.isModerator) {
9
- throw new McpError(ErrorCode.InvalidRequest, `Forbidden: ${toolName} requires Moderator rights.`);
7
+ export function checkHostPermission(toolName) {
8
+ if (!CONFIG.isHost) {
9
+ throw new McpError(ErrorCode.InvalidRequest, `Forbidden: ${toolName} requires Host rights.`);
10
10
  }
11
11
  }
@@ -0,0 +1,63 @@
1
+ # Architecture & Standards
2
+
3
+ ## 🏛️ Architecture
4
+
5
+ 1. **Nexus Room (Discussion)**: Unified public channel for all IDE assistants to coordinate across projects.
6
+ 2. **Asset Vault (Archives)**:
7
+ - **Manifest**: Technical details, billing, topology, and API specs for each project.
8
+ - **Internal Docs**: Detailed technical implementation plans.
9
+ - **Assets**: Local physical assets (Logos, UI screenshots, etc.).
10
+ 3. **Global Knowledge**:
11
+ - **Master Strategy**: Top-level strategic blueprint.
12
+ - **Global Docs**: Cross-project common documents (e.g., Coding Standards, Roadmaps).
13
+ 4. **Topology Engine**: Automated dependency graph analysis.
14
+
15
+ ## 💾 Data Persistence
16
+
17
+ Nexus stores all data in the local file system (customizable path), ensuring complete data sovereignty.
18
+
19
+ **Directory Structure Example**:
20
+ ```text
21
+ Nexus_Storage/
22
+ ├── global/
23
+ │ ├── blueprint.md # Master Strategy
24
+ │ ├── discussion.json # Global Chat History (fallback)
25
+ │ ├── docs_index.json # Global Docs Index
26
+ │ └── docs/ # Global Markdown Docs
27
+ │ ├── coding-standards.md
28
+ │ └── deployment-flow.md
29
+ ├── projects/
30
+ │ └── {project-id}/
31
+ │ ├── manifest.json # Project Metadata
32
+ │ ├── internal_blueprint.md # Technical Implementation Docs
33
+ │ └── assets/ # Binary Assets (images, PDFs)
34
+ ├── meetings/ # Meeting files (JSON fallback mode)
35
+ │ └── {meeting-id}.json
36
+ ├── registry.json # Global Project Index
37
+ ├── archives/ # Reserved for backups
38
+ └── nexus.db # SQLite Database (meetings, tasks, state)
39
+ ```
40
+
41
+ **Self-healing**: Core data files (e.g., `registry.json`, `discussion.json`) include automatic detection and repair mechanisms. If files are corrupted or missing, the system automatically rebuilds the initial state to ensure uninterrupted service.
42
+
43
+ **Concurrency Safety**: All write operations to shared files (`discussion.json`, `registry.json`) are protected by an `AsyncMutex` lock, preventing race conditions when multiple AI agents communicate simultaneously.
44
+
45
+ ## 🏷️ Project ID Conventions (Naming Standard)
46
+
47
+ To ensure clarity and prevent collisions in the flat local namespace, all Project IDs MUST follow the **Prefix Dictionary** format: `[prefix]_[project-name]`.
48
+
49
+ | Prefix | Category | Example |
50
+ | :--- | :--- | :--- |
51
+ | `web_` | Websites, landing pages, domain-based projects | `web_datafrog.io` |
52
+ | `api_` | Backend services, REST/gRPC APIs | `api_user-auth` |
53
+ | `chrome_` | Chrome extensions | `chrome_evisa-helper` |
54
+ | `vscode_` | VSCode extensions | `vscode_super-theme` |
55
+ | `mcp_` | MCP Servers and MCP-related tools | `mcp_github-repo` |
56
+ | `android_` | Native Android projects (Kotlin/Java) | `android_client-app` |
57
+ | `ios_` | Native iOS projects (Swift/ObjC) | `ios_client-app` |
58
+ | `flutter_` | **Mobile Cross-platform Special Case** | `flutter_unified-app` |
59
+ | `desktop_` | General desktop apps (Tauri, Electron, etc.) | `desktop_main-hub` |
60
+ | `lib_` | Shared libraries, SDKs, NPM/Python packages | `lib_crypto-core` |
61
+ | `bot_` | Bots (Discord, Slack, DingTalk, etc.) | `bot_auto-moderator` |
62
+ | `infra_` | Infrastructure as Code, CI/CD, DevOps scripts | `infra_k8s-config` |
63
+ | `doc_` | Pure technical handbooks, strategies, roadmaps | `doc_coding-guide` |
@@ -0,0 +1,43 @@
1
+ # 架构与标准 (Architecture & Standards)
2
+
3
+ ## 🏛️ 系统架构 (Architecture)
4
+
5
+ 1. **Nexus Room (讨论区)**: 所有 IDE 助手的统一公域频道,用于跨项目协调。
6
+ 2. **Asset Vault (归档库)**:
7
+ - **Manifest**: 每个项目的技术细节、计费、拓扑关系、API 规范。
8
+ - **Internal Docs**: 每个项目的详细技术实施方案。
9
+ - **Assets**: 本地物理素材存储(Logo/UI 截图等)。
10
+ 3. **Global Knowledge (全局知识库)**:
11
+ - **Master Strategy**: 顶层战略总纲。
12
+ - **Global Docs**: 跨项目的通用文档(如编码规范、路线图)。
13
+ 4. **Topology Engine**: 自动分析项目间的依赖关系图谱。
14
+
15
+ ## 💾 数据持久化 (Data Persistence)
16
+
17
+ Nexus 将所有数据存储在本地文件系统中(默认路径可配置),完全掌控数据主权。
18
+
19
+ **目录结构示例**:
20
+ ```text
21
+ Nexus_Storage/
22
+ ├── global/
23
+ │ ├── blueprint.md # Master Strategy
24
+ │ ├── discussion.json # 全局聊天历史 (fallback)
25
+ │ ├── docs_index.json # 全局文档索引
26
+ │ └── docs/ # 全局 Markdown 文档
27
+ │ ├── coding-standards.md
28
+ │ └── deployment-flow.md
29
+ ├── projects/
30
+ │ └── {project-id}/
31
+ │ ├── manifest.json # 项目元数据
32
+ │ ├── internal_blueprint.md # 技术实现文档
33
+ │ └── assets/ # 二进制资产 (图片、PDF)
34
+ ├── meetings/ # 会议文件 (JSON 回退模式)
35
+ │ └── {meeting-id}.json
36
+ ├── registry.json # 全局项目索引
37
+ ├── archives/ # 归档备份 (保留)
38
+ └── nexus.db # SQLite 数据库 (会议、任务、状态)
39
+ ```
40
+
41
+ **自我修复 (Self-healing)**: 核心数据文件(如 `registry.json`, `discussion.json`)具备自动检测与修复机制。如果文件损坏或意外丢失,系统会自动重建初始状态,确保服务不中断。
42
+
43
+ **多并发安全 (Concurrency Safety)**: 对共享文件(`discussion.json`, `registry.json`)的所有写入操作均受 `AsyncMutex` 锁保护,防止多个 AI 代理同时通信时发生竞争条件。
@@ -47,15 +47,15 @@ Nexus 采用 **Context7 风格** 的渐进加载模式,最大化 Token 效率
47
47
  ### 6. 战术会议 (Tactical Meetings)
48
48
  1. **发起**: `start_meeting(topic)`。
49
49
  2. **参与**: 发送 category 为 `DECISION` 的消息作为共识。
50
- 3. **结束**: `end_meeting(summary?)`。锁定历史(**Moderator only**)。
51
- 4. **归档**: `archive_meeting(meetingId)`(**Moderator only**)。
50
+ 3. **结束**: `end_meeting(summary?)`。锁定历史(**Host only**)。
51
+ 4. **归档**: `archive_meeting(meetingId)`(**Host only**)。
52
52
  5. **重开**: `reopen_meeting(meetingId)`。
53
53
 
54
54
  ---
55
55
 
56
56
  ## 🛡️ 角色说明
57
57
  - **Regular**: 拥有注册、同步、讨论和维护文档的完整权限。
58
- - **Moderator**: 额外拥有清理记录(`moderator_maintenance`)、结束/归档会议及物理删除(`moderator_delete_project`)的权限。
58
+ - **Host**: 管理员实例(通常是第一个启动的实例),额外拥有清理记录(`host_maintenance`)、结束/归档会议及物理删除(`host_delete_project`)的权限。
59
59
 
60
60
  ## ❌ 退出机制
61
61
  本系统是对本地磁盘的原子写入。请确保同步时提供清晰的 `internalDocs`,以便其他 Assistant 能够无缝接手。
@@ -2,6 +2,50 @@
2
2
 
3
3
  本项目的所有重大变更都将记录在此文件中。
4
4
 
5
+ ## [v0.3.0] - 2026-01-08
6
+
7
+ ### 🌐 全局 Hub 架构 (零配置多 IDE 协作)
8
+
9
+ 此版本引入了全自动的 Host 选举和全局 Hub 架构,无需任何配置即可实现多 IDE 无缝协作。
10
+
11
+ #### 自动 Host 选举 (基于端口)
12
+ - **端口范围 5688-5700**: 首个绑定端口的实例成为 Host,其余成为 Guest。
13
+ - **探测优先策略**: Guest 在尝试绑定前先扫描已有 Host,消除竞态条件。
14
+ - **Hello 握手协议**: `/hello` 端点验证 Nexus 身份,区分其他服务。
15
+ - **10 秒接管窗口**: 绑定失败后,Guest 等待 10 秒再探测加入胜者。
16
+
17
+ #### 全局 Hub (基于 SSE 通讯)
18
+ - **单一 Hub 架构**: 所有 IDE(不论哪个项目)都连接到同一个 Host,实现跨项目协作。
19
+ - **Stdio-to-SSE 代理**: Guest 透明地将 IDE 流量转发到 Host。
20
+ - **多会话路由**: Host 维护会话映射,支持多个 Guest 并发连接。
21
+ - **存储路径继承**: Host 广播 `rootStorage` 路径;Guest 继承它以实现无缝故障转移。
22
+
23
+ #### 心跳与看门狗 (高可用)
24
+ - **Host 心跳**: Host 每 30 秒发送 `: ping` 保持连接活跃。
25
+ - **Guest 看门狗**: Guest 监控活动;若 60 秒无响应,自动触发重选。
26
+ - **热故障转移**: 存活的 Guest 自动使用继承的存储路径升迁为新 Host。
27
+
28
+ #### 术语重构
29
+ - **Moderator → Host**: 所有代码、测试和文档已更新。
30
+ - **`isModerator` → `isHost`**: API 和配置属性已重命名。
31
+ - **`moderator_*` → `host_*`**: 工具名已更新(如 `host_maintenance`)。
32
+
33
+ #### 零配置体验
34
+ - **`--id` 弃用**: 实例 ID 现在自动从项目文件夹名派生。
35
+ - **`--host` 移除**: Host 角色通过端口绑定自动确定。
36
+ - **简化 CLI**: 只需 `npx @datafrog-io/n2n-nexus` 即可开始。
37
+
38
+ | 指标 | 之前 | 之后 |
39
+ |------|------|------|
40
+ | 必需 CLI 参数 | 2-3 | 0-1 |
41
+ | 手动 Host 设置 | 需要 | 自动 |
42
+ | 多 IDE 同步 | 基于文件 | SSE 实时 |
43
+ | 故障转移时间 | 手动重启 | < 10 秒 |
44
+
45
+ #### 测试覆盖
46
+ - 新增 `election.test.ts`,包含 9 个覆盖探测、绑定和竞争场景的测试用例。
47
+ - 全部 55 个测试通过。
48
+
5
49
  ## [v0.2.1] - 2026-01-01
6
50
 
7
51
  ### 🚀 Token 经济深度优化
@@ -49,6 +93,6 @@
49
93
  ## [v0.2.0] - 2025-12-31
50
94
 
51
95
  ### 🚀 任务原语系统 (Phase 2 & 3)
52
- - **异步深耕**: 关键阻塞操作 (`rename_project`, `moderator_delete_project`) 迁移至 Task 原语,支持背景级联更新。
96
+ - **异步深耕**: 关键阻塞操作 (`rename_project`, `host_delete_project`) 迁移至 Task 原语,支持背景级联更新。
53
97
  - **可追溯性**: 所有任务现在都支持 `source_meeting_id`,将执行结果与会议决策关联。
54
98
  - **进度展示**: 增加了进度追踪 (0.0-1.0) 和 `result_uri` 结构化输出。
package/docs/README_zh.md CHANGED
@@ -10,53 +10,15 @@
10
10
 
11
11
  > **支持的 IDE:** Claude Code · Claude Desktop · VS Code · Cursor · Windsurf · Zed · JetBrains · Theia · Google Antigravity
12
12
 
13
- 📖 **文档导航:** [English README](../README.md) | [更新日志](TODO_zh.md) | [AI 助手指南](ASSISTANT_GUIDE.md)
14
-
15
- ## 🏛️ 系统架构 (Architecture)
16
-
17
- 1. **Nexus Room (讨论区)**: 所有 IDE 助手的统一公域频道,用于跨项目协调。
18
- 2. **Asset Vault (归档库)**:
19
- - **Manifest**: 每个项目的技术细节、计费、拓扑关系、API 规范。
20
- - **Internal Docs**: 每个项目的详细技术实施方案。
21
- - **Assets**: 本地物理素材存储(Logo/UI 截图等)。
22
- 3. **Global Knowledge (全局知识库)**:
23
- - **Master Strategy**: 顶层战略总纲。
24
- - **Global Docs**: 跨项目的通用文档(如编码规范、路线图)。
25
- 4. **Topology Engine**: 自动分析项目间的依赖关系图谱。
26
-
27
- ## � 数据持久化 (Data Persistence)
28
-
29
- Nexus 将所有数据存储在本地文件系统中(默认路径可配置),完全掌控数据主权。
30
-
31
- **目录结构示例**:
32
- ```text
33
- Nexus_Storage/
34
- ├── global/
35
- │ ├── blueprint.md # Master Strategy
36
- │ ├── discussion.json # 全局聊天历史 (fallback)
37
- │ ├── docs_index.json # 全局文档索引
38
- │ └── docs/ # 全局 Markdown 文档
39
- │ ├── coding-standards.md
40
- │ └── deployment-flow.md
41
- ├── projects/
42
- │ └── {project-id}/
43
- │ ├── manifest.json # 项目元数据
44
- │ ├── internal_blueprint.md # 技术实现文档
45
- │ └── assets/ # 二进制资产 (图片、PDF)
46
- ├── meetings/ # 会议文件 (JSON 回退模式)
47
- │ └── {meeting-id}.json
48
- ├── registry.json # 全局项目索引
49
- ├── archives/ # 归档备份 (保留)
50
- └── nexus.db # SQLite 数据库 (会议、任务、状态)
51
- ```
13
+ 📖 **文档导航:** [English README](../README.md) | [更新日志](TODO_zh.md) | [AI 助手指南](ASSISTANT_GUIDE.md) | [架构文档](ARCHITECTURE_zh.md)
14
+
52
15
 
53
- **自我修复 (Self-healing)**: 核心数据文件(如 `registry.json`, `discussion.json`)具备自动检测与修复机制。如果文件损坏或意外丢失,系统会自动重建初始状态,确保服务不中断。
54
16
 
55
17
  ## �🛠️ 工具集 (Toolset)
56
18
 
57
19
  ### A. 会话与上下文 (Session)
58
20
  - `register_session_context`: 声明当前 IDE 工作的项目 ID,解锁写权限。
59
- - `mcp://nexus/session`: 查看当前身份、角色(Moderator/Regular)及活动项目。
21
+ - `mcp://nexus/session`: 查看当前身份、角色(Host/Regular)及活动项目。
60
22
 
61
23
  ### B. 项目资产管理 (Project Assets)
62
24
  - `sync_project_assets`: **[核心/异步]** 提交完整的项目 Manifest 和内部技术文档。返回 `taskId`。
@@ -76,8 +38,8 @@ Nexus_Storage/
76
38
  ### D. 会议管理 (Tactical Meetings)
77
39
  - `start_meeting`: 开启新的战术讨论会议。
78
40
  - `reopen_meeting`: 重新开启已“关闭”或“归档”的会议。
79
- - `end_meeting`: 结束会议,锁定历史记录 (**仅限管理员 Moderator**)。
80
- - `archive_meeting`: 将已结束的会议移至存档 (**仅限管理员 Moderator**)。
41
+ - `end_meeting`: 结束会议,锁定历史记录 (**仅限 Host**)。
42
+ - `archive_meeting`: 将已结束的会议移至存档 (**仅限 Host**)。
81
43
 
82
44
  ### E. 任务管理 (Phase 2 - 异步)
83
45
  - `create_task`: 创建新的后台任务。关联会议以实现溯源。
@@ -86,9 +48,9 @@ Nexus_Storage/
86
48
  - `update_task`: 更新任务进度或结果(通常供 Worker 调用)。
87
49
  - `cancel_task`: 取消待处理或运行中的任务。
88
50
 
89
- ### F. 管理员工具 (仅限 Moderator)
90
- - `moderator_maintenance`: 清理或修剪系统日志。
91
- - `moderator_delete_project`: 彻底删除项目及其所有资产。
51
+ ### F. 主持者工具 (仅限 Host)
52
+ - `host_maintenance`: 清理或修剪系统日志。
53
+ - `host_delete_project`: 彻底删除项目及其所有资产。
92
54
 
93
55
  ## 📄 资源 URI (Resources)
94
56
 
@@ -108,13 +70,39 @@ Nexus_Storage/
108
70
  - `mcp://nexus/docs/{docId}`: 读取特定的全局共享文档。
109
71
  - `mcp://nexus/meetings/{meetingId}`: 特定会议的完整记录。
110
72
 
73
+ ## 🌐 全局 Hub 架构
74
+
75
+ **v0.3.0** 引入了全自动、零配置的协作架构:
76
+
77
+ ```
78
+ ┌─────────────────────────────────────────────────────────────┐
79
+ │ 全局 Nexus Hub │
80
+ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
81
+ │ │ Cursor │ │ VS Code │ │ Claude │ │ Zed │ │
82
+ │ │ (Guest) │ │ (Guest) │ │ (Host) │ │ (Guest) │ │
83
+ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
84
+ │ │ │ │ │ │
85
+ │ └─────────────┴──────┬──────┴─────────────┘ │
86
+ │ │ SSE │
87
+ │ ┌───────▼───────┐ │
88
+ │ │ 端口 5688 │ │
89
+ │ │ (自动选举) │ │
90
+ │ └───────────────┘ │
91
+ └─────────────────────────────────────────────────────────────┘
92
+ ```
93
+
94
+ - **零配置**: 只需运行 `npx @datafrog-io/n2n-nexus` — 无需 `--id` 或 `--host`。
95
+ - **自动选举**: 首个实例绑定 5688 端口成为 Host;其余自动加入为 Guest。
96
+ - **跨项目同步**: 所有 IDE 共享同一个 Hub,实现实时跨项目会议。
97
+ - **热故障转移**: 若 Host 断开,Guest 将在 10 秒内自动升迁。
98
+
111
99
  ## 🚀 快速启动
112
100
 
113
101
  ### MCP 配置(推荐)
114
102
 
115
103
  在你的 MCP 配置文件中(如 `claude_desktop_config.json` 或 Cursor MCP 设置)添加:
116
104
 
117
- #### 主持者(管理员 AI
105
+ #### 主导 AI
118
106
  ```json
119
107
  {
120
108
  "mcpServers": {
@@ -123,8 +111,6 @@ Nexus_Storage/
123
111
  "args": [
124
112
  "-y",
125
113
  "@datafrog-io/n2n-nexus",
126
- "--id", "Master-AI",
127
- "--moderator",
128
114
  "--root", "D:/DevSpace/Nexus_Storage"
129
115
  ]
130
116
  }
@@ -132,7 +118,7 @@ Nexus_Storage/
132
118
  }
133
119
  ```
134
120
 
135
- #### 普通 AI
121
+ #### 协同 AI (Guest)
136
122
  ```json
137
123
  {
138
124
  "mcpServers": {
@@ -141,7 +127,6 @@ Nexus_Storage/
141
127
  "args": [
142
128
  "-y",
143
129
  "@datafrog-io/n2n-nexus",
144
- "--id", "Assistant-AI",
145
130
  "--root", "D:/DevSpace/Nexus_Storage"
146
131
  ]
147
132
  }
@@ -152,11 +137,9 @@ Nexus_Storage/
152
137
  ### 命令行参数
153
138
  | 参数 | 说明 | 默认值 |
154
139
  |------|------|--------|
155
- | `--id` | 当前 AI 助手的实例标识符 | `Assistant` |
156
- | `--moderator` | 授予此实例管理员权限 | `false` |
157
140
  | `--root` | 本地数据存储路径 | `./storage` |
158
141
 
159
- > **注意:** 仅带有 `--moderator` 标志的实例可使用管理员工具(如 `moderator_maintenance` 和 `moderator_delete_project`)。
142
+ > **注意:** 实例 ID(默认为当前项目文件夹名称)和 Host 身份将根据启动顺序自动生成。
160
143
 
161
144
  ### 本地开发
162
145
  ```bash
@@ -164,7 +147,7 @@ git clone https://github.com/n2ns/n2n-nexus.git
164
147
  cd n2n-nexus
165
148
  npm install
166
149
  npm run build
167
- npm start -- --id Master-AI --root ./my-storage
150
+ npm start -- --root ./my-storage
168
151
  ```
169
152
 
170
153
  ---
@@ -187,4 +170,13 @@ npm start -- --id Master-AI --root ./my-storage
187
170
  > *这就是 AI 原生开发的协作方式。*
188
171
 
189
172
  ---
190
- © 2025 datafrog.io. Built for Local-Only AI Workflows.
173
+
174
+ ## ⭐ 支持本项目
175
+
176
+ 如果 **n2ns Nexus** 帮助您构建了更好的 AI 工作流,考虑给我们一个 Star 吧!您的支持是我们持续改进的动力。
177
+
178
+ [![Star on GitHub](https://img.shields.io/github/stars/n2ns/n2n-nexus?style=social)](https://github.com/n2ns/n2n-nexus)
179
+
180
+ ---
181
+
182
+ © 2026 datafrog.io. Built for Local-Only AI Workflows.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datafrog-io/n2n-nexus",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Unified Project Asset & Collaboration Hub (MCP Server) designed for AI agent coordination, featuring structured metadata, real-time messaging, and dependency topology.",
5
5
  "main": "build/index.js",
6
6
  "type": "module",