@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 +47 -80
- package/build/config.js +176 -4
- package/build/index.js +133 -16
- package/build/resources/index.js +2 -2
- package/build/tools/definitions.js +7 -7
- package/build/tools/handlers.js +19 -19
- package/build/tools/schemas.js +12 -12
- package/build/utils/auth.js +5 -5
- package/docs/ARCHITECTURE.md +63 -0
- package/docs/ARCHITECTURE_zh.md +43 -0
- package/docs/ASSISTANT_GUIDE.md +3 -3
- package/docs/CHANGELOG_zh.md +45 -1
- package/docs/README_zh.md +48 -56
- package/package.json +1 -1
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 (
|
|
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 (**
|
|
105
|
-
- `archive_meeting`: Move closed meetings to cold storage (**
|
|
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.
|
|
115
|
-
- `
|
|
116
|
-
- `
|
|
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
|
-
####
|
|
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
|
-
####
|
|
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:**
|
|
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 -- --
|
|
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
|
-
|
|
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
|
+
[](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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
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 {
|
|
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("
|
|
66
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
// Announce presence
|
|
149
|
-
try {
|
|
146
|
+
if (CONFIG.isHost && hostServer) {
|
|
147
|
+
// --- HOST MODE: Central Hub ---
|
|
150
148
|
await StorageManager.init();
|
|
151
|
-
|
|
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
|
-
|
|
156
|
-
|
|
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
|
}
|
package/build/resources/index.js
CHANGED
|
@@ -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.
|
|
26
|
-
|
|
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: "[
|
|
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: "[
|
|
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
|
-
// ---
|
|
261
|
+
// --- Host (Host Only) ---
|
|
262
262
|
{
|
|
263
|
-
name: "
|
|
264
|
-
description: "[
|
|
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: "
|
|
276
|
-
description: "[ASYNC][
|
|
275
|
+
name: "host_delete_project",
|
|
276
|
+
description: "[ASYNC][Host] Delete project. Irreversible. Returns taskId.",
|
|
277
277
|
inputSchema: {
|
|
278
278
|
type: "object",
|
|
279
279
|
properties: {
|
package/build/tools/handlers.js
CHANGED
|
@@ -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 "
|
|
48
|
+
case "host_delete_project":
|
|
49
49
|
return handleRemoveProject(validatedArgs, ctx);
|
|
50
|
-
case "
|
|
51
|
-
return
|
|
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:
|
|
324
|
+
* ASYNC: host_delete_project now returns a taskId.
|
|
325
325
|
*/
|
|
326
326
|
async function handleRemoveProject(args, ctx) {
|
|
327
|
-
// Defense-in-Depth: Explicit
|
|
328
|
-
if (!CONFIG.
|
|
329
|
-
throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Only
|
|
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: "
|
|
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
|
|
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
|
|
384
|
-
// Defense-in-Depth: Explicit
|
|
385
|
-
if (!CONFIG.
|
|
386
|
-
throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Only
|
|
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
|
|
438
|
-
if (!CONFIG.
|
|
439
|
-
throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Only
|
|
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
|
|
462
|
-
if (!CONFIG.
|
|
463
|
-
throw new McpError(ErrorCode.InvalidRequest, "Permission denied: Only
|
|
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 {
|
package/build/tools/schemas.js
CHANGED
|
@@ -111,16 +111,16 @@ export const RenameProjectSchema = z.object({
|
|
|
111
111
|
newId: ProjectIdSchema
|
|
112
112
|
});
|
|
113
113
|
/**
|
|
114
|
-
* 15.
|
|
114
|
+
* 15. host_maintenance
|
|
115
115
|
*/
|
|
116
|
-
export const
|
|
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.
|
|
121
|
+
* 16. host_delete_project
|
|
122
122
|
*/
|
|
123
|
-
export const
|
|
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
|
-
|
|
234
|
-
description: "[
|
|
235
|
-
schema:
|
|
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
|
-
|
|
238
|
-
description: "[ASYNC][
|
|
239
|
-
schema:
|
|
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
|
|
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
|
|
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: {
|
package/build/utils/auth.js
CHANGED
|
@@ -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
|
|
5
|
-
* @throws McpError if current session is not in
|
|
4
|
+
* Validates host permissions for privileged tools.
|
|
5
|
+
* @throws McpError if current session is not in Host mode.
|
|
6
6
|
*/
|
|
7
|
-
export function
|
|
8
|
-
if (!CONFIG.
|
|
9
|
-
throw new McpError(ErrorCode.InvalidRequest, `Forbidden: ${toolName} requires
|
|
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 代理同时通信时发生竞争条件。
|
package/docs/ASSISTANT_GUIDE.md
CHANGED
|
@@ -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?)`。锁定历史(**
|
|
51
|
-
4. **归档**: `archive_meeting(meetingId)`(**
|
|
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
|
-
- **
|
|
58
|
+
- **Host**: 管理员实例(通常是第一个启动的实例),额外拥有清理记录(`host_maintenance`)、结束/归档会议及物理删除(`host_delete_project`)的权限。
|
|
59
59
|
|
|
60
60
|
## ❌ 退出机制
|
|
61
61
|
本系统是对本地磁盘的原子写入。请确保同步时提供清晰的 `internalDocs`,以便其他 Assistant 能够无缝接手。
|
package/docs/CHANGELOG_zh.md
CHANGED
|
@@ -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`, `
|
|
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`: 查看当前身份、角色(
|
|
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`: 结束会议,锁定历史记录 (
|
|
80
|
-
- `archive_meeting`: 将已结束的会议移至存档 (
|
|
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.
|
|
90
|
-
- `
|
|
91
|
-
- `
|
|
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
|
-
####
|
|
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
|
-
####
|
|
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
|
-
> **注意:**
|
|
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 -- --
|
|
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
|
-
|
|
173
|
+
|
|
174
|
+
## ⭐ 支持本项目
|
|
175
|
+
|
|
176
|
+
如果 **n2ns Nexus** 帮助您构建了更好的 AI 工作流,考虑给我们一个 Star 吧!您的支持是我们持续改进的动力。
|
|
177
|
+
|
|
178
|
+
[](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.
|
|
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",
|