n2n-nexus 0.4.2
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/CHANGELOG.md +358 -0
- package/LICENSE +201 -0
- package/README.md +286 -0
- package/build/client/nexus-client.js +71 -0
- package/build/config/cli.js +11 -0
- package/build/config/index.js +16 -0
- package/build/config/paths.js +38 -0
- package/build/constants.js +22 -0
- package/build/daemon/index.js +41 -0
- package/build/daemon/server.js +791 -0
- package/build/index.js +47 -0
- package/build/server/nexus.js +98 -0
- package/build/storage/docs.js +74 -0
- package/build/storage/index.js +105 -0
- package/build/storage/logs.js +60 -0
- package/build/storage/meetings.js +276 -0
- package/build/storage/paths.js +26 -0
- package/build/storage/projects.js +75 -0
- package/build/storage/registry.js +230 -0
- package/build/storage/sqlite-meeting.js +311 -0
- package/build/storage/sqlite.js +141 -0
- package/build/storage/store.js +153 -0
- package/build/storage/tasks.js +212 -0
- package/build/types.js +1 -0
- package/build/utils/async-mutex.js +36 -0
- package/docs/ARCHITECTURE.md +205 -0
- package/docs/ARCHITECTURE_zh.md +205 -0
- package/docs/ASSISTANT_GUIDE.md +120 -0
- package/docs/README_zh.md +285 -0
- package/llms.txt +46 -0
- package/package.json +90 -0
package/README.md
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# n2n-nexus
|
|
2
|
+
|
|
3
|
+
Local-first MCP coordination hub from N2NS Lab for multi-AI assistant collaboration across IDEs, machines, and projects.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/n2n-nexus)
|
|
6
|
+
[](https://www.npmjs.com/package/n2n-nexus)
|
|
7
|
+
[](https://github.com/n2ns/n2n-nexus/blob/main/LICENSE)
|
|
8
|
+
[](https://modelcontextprotocol.io)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
[](https://datafrog.io)
|
|
11
|
+
|
|
12
|
+
[中文版](./docs/README_zh.md)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
> **One daemon. Many assistants. Shared project state.**
|
|
17
|
+
|
|
18
|
+
n2n-nexus is an open-source Model Context Protocol (MCP) coordination server for teams and developers who use multiple AI coding assistants. A long-running local daemon owns project state, meetings, messages, tasks, shared docs, and project assets. Lightweight MCP adapters connect each IDE or assistant to the same daemon.
|
|
19
|
+
|
|
20
|
+
Use it when Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, JetBrains, or other MCP-enabled clients need a shared local coordination layer instead of isolated per-chat context.
|
|
21
|
+
|
|
22
|
+
## Search-friendly positioning
|
|
23
|
+
|
|
24
|
+
If you are searching for:
|
|
25
|
+
|
|
26
|
+
- multi-agent MCP coordination
|
|
27
|
+
- multi-AI assistant collaboration
|
|
28
|
+
- local MCP coordination hub
|
|
29
|
+
- shared project context for AI coding agents
|
|
30
|
+
- cross-IDE MCP collaboration server
|
|
31
|
+
- local-first project registry for AI assistants
|
|
32
|
+
- meeting and task coordination for coding agents
|
|
33
|
+
|
|
34
|
+
n2n-nexus is designed for these goals: shared daemon state, stateless MCP adapters, project-aware collaboration, local storage, and cross-environment IDE workflows.
|
|
35
|
+
|
|
36
|
+
## What Is n2n-nexus?
|
|
37
|
+
|
|
38
|
+
n2n-nexus gives AI assistants a shared coordination workspace. Instead of each assistant keeping its own isolated conversation state, all connected MCP clients can read and write to the same local daemon.
|
|
39
|
+
|
|
40
|
+
**TL;DR**
|
|
41
|
+
|
|
42
|
+
- **Install**: `npx n2n-nexus daemon --port 5688`
|
|
43
|
+
- **Protocol**: Model Context Protocol (MCP), adapter-to-daemon HTTP bridge
|
|
44
|
+
- **Storage**: local filesystem plus SQLite under `~/.n2n-nexus` by default
|
|
45
|
+
- **Best for**: multi-assistant coding sessions, project handoffs, shared decisions, meeting notes, async tasks, project manifests
|
|
46
|
+
- **Not for**: cloud team chat, public project management SaaS, source code indexing, vector search, or remote database hosting
|
|
47
|
+
|
|
48
|
+
## Why Use It?
|
|
49
|
+
|
|
50
|
+
- Coordinate multiple AI assistants working on the same project.
|
|
51
|
+
- Share project manifests, internal notes, assets, and topology across IDEs.
|
|
52
|
+
- Keep decisions, proposals, and updates in a local meeting/message log.
|
|
53
|
+
- Run the daemon once and connect adapters from Windows, WSL, SSH hosts, VMs, or multiple editors.
|
|
54
|
+
- Avoid giving each assistant broad filesystem or backend access just to exchange context.
|
|
55
|
+
|
|
56
|
+
## Architecture
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
┌──────────────────────────────────────┐
|
|
60
|
+
│ n2n-nexus daemon │
|
|
61
|
+
│ Standalone HTTP server · always on │
|
|
62
|
+
│ Owns data, tools, tasks, messages │
|
|
63
|
+
└──────────────┬───────────────────────┘
|
|
64
|
+
│ HTTP (NEXUS_ENDPOINT)
|
|
65
|
+
┌───────┼───────┐
|
|
66
|
+
▼ ▼ ▼
|
|
67
|
+
MCP-A MCP-B MCP-C
|
|
68
|
+
(Win) (WSL) (SSH/VM)
|
|
69
|
+
Stateless adapter per IDE
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- **Daemon is the source of truth**: start it once and keep it running.
|
|
73
|
+
- **MCP adapters are stateless**: each IDE starts an adapter through `npx`, then forwards tool calls to the daemon.
|
|
74
|
+
- **Cross-environment by design**: point `NEXUS_ENDPOINT` at the same daemon from different machines or shells. The daemon binds to localhost by default; use `--host 0.0.0.0` only on a trusted network.
|
|
75
|
+
|
|
76
|
+
## Quick Start
|
|
77
|
+
|
|
78
|
+
### 1. Start the daemon
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npx n2n-nexus daemon --port 5688
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 2. Configure an MCP client
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"mcpServers": {
|
|
89
|
+
"n2n-nexus": {
|
|
90
|
+
"command": "npx",
|
|
91
|
+
"args": ["-y", "n2n-nexus", "mcp"],
|
|
92
|
+
"env": {
|
|
93
|
+
"NEXUS_ENDPOINT": "http://127.0.0.1:5688"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The adapter can start before the daemon. It loads the daemon tool list once the daemon becomes reachable.
|
|
101
|
+
|
|
102
|
+
### Cross-environment endpoint examples
|
|
103
|
+
|
|
104
|
+
| Scenario | `NEXUS_ENDPOINT` |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| Same machine | `http://127.0.0.1:5688` |
|
|
107
|
+
| WSL IDE to Windows daemon | `http://host.docker.internal:5688` |
|
|
108
|
+
| Windows IDE to WSL daemon | `http://<WSL-IP>:5688` |
|
|
109
|
+
| Remote machine | `http://<Server-IP>:5688` |
|
|
110
|
+
|
|
111
|
+
## Toolset
|
|
112
|
+
|
|
113
|
+
### Session and context
|
|
114
|
+
|
|
115
|
+
- `register_session_context`: declare the active project ID.
|
|
116
|
+
|
|
117
|
+
### Project asset management
|
|
118
|
+
|
|
119
|
+
- `sync_project_assets`: submit a project manifest and internal docs.
|
|
120
|
+
- `update_project`: patch a project manifest.
|
|
121
|
+
- `rename_project`: rename a project ID and update relations.
|
|
122
|
+
- `upload_project_asset`: upload binary or text assets.
|
|
123
|
+
- `search_projects`: search the project registry.
|
|
124
|
+
- `get_global_topology`: inspect project topology and dependencies.
|
|
125
|
+
|
|
126
|
+
### Messaging and collaboration
|
|
127
|
+
|
|
128
|
+
- `send_message`: post meeting or global messages.
|
|
129
|
+
- `read_messages`: read unread messages incrementally.
|
|
130
|
+
- `update_global_strategy`: update the master strategy document.
|
|
131
|
+
- `sync_global_doc`: create or update shared docs.
|
|
132
|
+
|
|
133
|
+
### Meeting management
|
|
134
|
+
|
|
135
|
+
- `start_meeting`: open a meeting session.
|
|
136
|
+
- `end_meeting`: close and lock a meeting.
|
|
137
|
+
- `archive_meeting`: move a closed meeting to archive.
|
|
138
|
+
- `reopen_meeting`: reactivate a closed or archived meeting.
|
|
139
|
+
|
|
140
|
+
### Async task management
|
|
141
|
+
|
|
142
|
+
- `create_task`: create a background task.
|
|
143
|
+
- `get_task`: poll task status and result.
|
|
144
|
+
- `list_tasks`: list tasks by status.
|
|
145
|
+
- `cancel_task`: cancel a pending or running task.
|
|
146
|
+
|
|
147
|
+
### Maintenance
|
|
148
|
+
|
|
149
|
+
- `host_maintenance`: prune or clear system logs.
|
|
150
|
+
- `host_delete_project`: delete a project and assets.
|
|
151
|
+
|
|
152
|
+
## Data Storage
|
|
153
|
+
|
|
154
|
+
Default storage root:
|
|
155
|
+
|
|
156
|
+
| Platform | Path |
|
|
157
|
+
| --- | --- |
|
|
158
|
+
| Linux / WSL | `~/.n2n-nexus` |
|
|
159
|
+
| Windows | `%USERPROFILE%\.n2n-nexus` |
|
|
160
|
+
| macOS | `~/.n2n-nexus` |
|
|
161
|
+
|
|
162
|
+
Override with `--root <path>` or `NEXUS_ROOT`.
|
|
163
|
+
|
|
164
|
+
```text
|
|
165
|
+
~/.n2n-nexus/
|
|
166
|
+
├── global/
|
|
167
|
+
│ ├── blueprint.md
|
|
168
|
+
│ ├── docs_index.json
|
|
169
|
+
│ └── docs/
|
|
170
|
+
├── projects/
|
|
171
|
+
│ └── {project-id}/
|
|
172
|
+
│ ├── manifest.json
|
|
173
|
+
│ ├── internal_blueprint.md
|
|
174
|
+
│ └── assets/
|
|
175
|
+
├── registry.json
|
|
176
|
+
└── nexus.db
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Project ID Conventions
|
|
180
|
+
|
|
181
|
+
Project IDs follow `[prefix]_[name]`.
|
|
182
|
+
|
|
183
|
+
| Prefix | Category | Example |
|
|
184
|
+
| --- | --- | --- |
|
|
185
|
+
| `web_` | Websites | `web_datafrog.io` |
|
|
186
|
+
| `api_` | Backend services | `api_user-auth` |
|
|
187
|
+
| `mcp_` | MCP servers | `mcp_nexus` |
|
|
188
|
+
| `lib_` | Libraries / SDKs | `lib_crypto-core` |
|
|
189
|
+
| `chrome_` | Chrome extensions | `chrome_evisa-helper` |
|
|
190
|
+
| `vscode_` | VS Code extensions | `vscode_super-theme` |
|
|
191
|
+
| `desktop_` | Desktop apps | `desktop_main-hub` |
|
|
192
|
+
| `infra_` | Infrastructure / DevOps | `infra_k8s-config` |
|
|
193
|
+
| `doc_` | Documentation | `doc_coding-guide` |
|
|
194
|
+
|
|
195
|
+
## CLI Reference
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
# Start daemon
|
|
199
|
+
n2n-nexus daemon [--port 5688] [--root ~/.n2n-nexus] [--host 127.0.0.1]
|
|
200
|
+
|
|
201
|
+
# Start MCP adapter
|
|
202
|
+
NEXUS_ENDPOINT=http://127.0.0.1:5688 n2n-nexus mcp
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Environment variables
|
|
206
|
+
|
|
207
|
+
| Variable | Description | Default |
|
|
208
|
+
| --- | --- | --- |
|
|
209
|
+
| `NEXUS_ENDPOINT` | Daemon URL for MCP adapter | `http://127.0.0.1:5688` |
|
|
210
|
+
| `NEXUS_ROOT` | Storage root for daemon | `~/.n2n-nexus` |
|
|
211
|
+
| `NEXUS_HOST` | Daemon bind host | `127.0.0.1` |
|
|
212
|
+
| `NEXUS_INSTANCE_ID` | Override MCP instance ID | auto-generated |
|
|
213
|
+
|
|
214
|
+
## Security and governance notes
|
|
215
|
+
|
|
216
|
+
- n2n-nexus stores coordination data locally by default.
|
|
217
|
+
- Do not put secrets, credentials, customer data, or private tokens in meeting messages or project assets unless your local policy allows it.
|
|
218
|
+
- Destructive tools such as project deletion should be used through explicit review workflows.
|
|
219
|
+
- Exposing the daemon beyond localhost is an operational choice; restrict network access when using remote endpoints.
|
|
220
|
+
- Treat project manifests and internal docs as potentially sensitive implementation data.
|
|
221
|
+
|
|
222
|
+
## Real-world example
|
|
223
|
+
|
|
224
|
+
The docs include a sample multi-agent session where several AI assistants collaborated on architecture and protocol decisions:
|
|
225
|
+
|
|
226
|
+
| File | Description |
|
|
227
|
+
| --- | --- |
|
|
228
|
+
| [Meeting Minutes](docs/MEETING_MINUTES_2025-12-29.md) | Structured decisions and test notes |
|
|
229
|
+
| [Discussion Log](docs/discussion_2025-12-29_en.md) | Human-readable discussion transcript |
|
|
230
|
+
|
|
231
|
+
## Local Development
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
git clone https://github.com/n2ns/n2n-nexus.git
|
|
235
|
+
cd n2n-nexus
|
|
236
|
+
npm install
|
|
237
|
+
npm run build
|
|
238
|
+
|
|
239
|
+
# Run daemon
|
|
240
|
+
node build/index.js daemon --root /tmp/nexus-test --port 5688
|
|
241
|
+
|
|
242
|
+
# Run MCP adapter
|
|
243
|
+
NEXUS_ENDPOINT=http://127.0.0.1:5688 node build/index.js mcp
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## FAQ
|
|
247
|
+
|
|
248
|
+
### Is n2n-nexus a project management SaaS?
|
|
249
|
+
|
|
250
|
+
No. It is a local MCP coordination server for AI assistants. It stores state locally and exposes MCP tools to connected clients.
|
|
251
|
+
|
|
252
|
+
### Does it replace n2n-memory?
|
|
253
|
+
|
|
254
|
+
No. `n2n-memory` is repository-local memory for one project. `n2n-nexus` is a shared coordination hub for multiple assistants, project manifests, messages, meetings, and tasks.
|
|
255
|
+
|
|
256
|
+
### Does it work with Claude Desktop, Cursor, VS Code, and other IDEs?
|
|
257
|
+
|
|
258
|
+
Yes, when the client supports local MCP command servers. The adapter is started by the IDE and connects to the local daemon through `NEXUS_ENDPOINT`.
|
|
259
|
+
|
|
260
|
+
### Can it coordinate assistants across Windows and WSL?
|
|
261
|
+
|
|
262
|
+
Yes. Run the daemon in one environment and point each adapter at it using `NEXUS_ENDPOINT`.
|
|
263
|
+
|
|
264
|
+
### Does it send data to the cloud?
|
|
265
|
+
|
|
266
|
+
No cloud service is required. Data is stored under the configured local root. If you expose the daemon to a remote machine, that is your own network configuration.
|
|
267
|
+
|
|
268
|
+
### Is this a vector database or code indexer?
|
|
269
|
+
|
|
270
|
+
No. n2n-nexus coordinates project metadata, messages, meetings, docs, tasks, and assets. It is not a semantic code search engine.
|
|
271
|
+
|
|
272
|
+
## Related docs
|
|
273
|
+
|
|
274
|
+
- [Architecture](./docs/ARCHITECTURE.md)
|
|
275
|
+
- [AI Assistant Guide](./docs/ASSISTANT_GUIDE.md)
|
|
276
|
+
- [Chinese README](./docs/README_zh.md)
|
|
277
|
+
- [Changelog](./CHANGELOG.md)
|
|
278
|
+
- [llms.txt](./llms.txt)
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
This project is licensed under the [Apache-2.0 License](./LICENSE).
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
Built by N2NS Lab, short for Next-to-Native Systems Lab, Datafrog's open-source lab for practical AI developer tools.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import https from "node:https";
|
|
3
|
+
import { URL } from "node:url";
|
|
4
|
+
function normalizeEndpoint(endpoint) {
|
|
5
|
+
const raw = endpoint || process.env.NEXUS_ENDPOINT || "http://127.0.0.1:5688";
|
|
6
|
+
if (/^https?:\/\//i.test(raw))
|
|
7
|
+
return raw;
|
|
8
|
+
return `http://${raw}`;
|
|
9
|
+
}
|
|
10
|
+
export class NexusClient {
|
|
11
|
+
endpoint;
|
|
12
|
+
timeoutMs;
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this.endpoint = normalizeEndpoint(options.endpoint);
|
|
15
|
+
this.timeoutMs = options.timeoutMs ?? 5000;
|
|
16
|
+
}
|
|
17
|
+
request(method, path, body) {
|
|
18
|
+
const url = new URL(path, this.endpoint);
|
|
19
|
+
const payload = body !== undefined ? JSON.stringify(body) : undefined;
|
|
20
|
+
const transport = url.protocol === "https:" ? https : http;
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const req = transport.request({
|
|
23
|
+
method,
|
|
24
|
+
protocol: url.protocol,
|
|
25
|
+
hostname: url.hostname,
|
|
26
|
+
port: url.port,
|
|
27
|
+
path: `${url.pathname}${url.search}`,
|
|
28
|
+
headers: {
|
|
29
|
+
"Content-Type": "application/json",
|
|
30
|
+
...(payload ? { "Content-Length": Buffer.byteLength(payload) } : {})
|
|
31
|
+
},
|
|
32
|
+
timeout: this.timeoutMs
|
|
33
|
+
}, (res) => {
|
|
34
|
+
let data = "";
|
|
35
|
+
res.on("data", chunk => { data += chunk; });
|
|
36
|
+
res.on("end", () => {
|
|
37
|
+
try {
|
|
38
|
+
const parsed = data ? JSON.parse(data) : {};
|
|
39
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
40
|
+
reject(new Error(parsed.error || `HTTP ${res.statusCode}`));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
resolve(parsed);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
reject(new Error(`Invalid JSON response: ${data}`));
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
req.on("error", reject);
|
|
52
|
+
req.on("timeout", () => req.destroy(new Error("Request timeout")));
|
|
53
|
+
if (payload)
|
|
54
|
+
req.write(payload);
|
|
55
|
+
req.end();
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async health() {
|
|
59
|
+
return this.request("GET", "/health");
|
|
60
|
+
}
|
|
61
|
+
async fetchTools() {
|
|
62
|
+
const res = await this.request("GET", "/api/tools");
|
|
63
|
+
return res.tools;
|
|
64
|
+
}
|
|
65
|
+
async callTool(name, args, instanceId) {
|
|
66
|
+
const res = await this.request("POST", "/api/tools/call", { tool: name, args: args || {}, instanceId: instanceId || "unknown" });
|
|
67
|
+
if (!res.ok)
|
|
68
|
+
throw new Error(res.error || "Tool call failed");
|
|
69
|
+
return res.result;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Argument Parsing
|
|
3
|
+
*/
|
|
4
|
+
const args = process.argv.slice(2);
|
|
5
|
+
export function getArg(k) {
|
|
6
|
+
const i = args.indexOf(k);
|
|
7
|
+
return i !== -1 && args[i + 1] ? args[i + 1] : "";
|
|
8
|
+
}
|
|
9
|
+
export function hasFlag(k) {
|
|
10
|
+
return args.includes(k) || args.includes(k.charAt(1) === "-" ? k : k.substring(0, 2));
|
|
11
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
const pkgPath = path.resolve(__dirname, "../../package.json");
|
|
6
|
+
export const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
7
|
+
export function getRootPath() {
|
|
8
|
+
// Priority: --root CLI arg → NEXUS_ROOT env → ~/.n2n-nexus
|
|
9
|
+
const argIndex = process.argv.indexOf("--root");
|
|
10
|
+
if (argIndex !== -1 && process.argv[argIndex + 1]) {
|
|
11
|
+
return process.argv[argIndex + 1];
|
|
12
|
+
}
|
|
13
|
+
if (process.env.NEXUS_ROOT)
|
|
14
|
+
return process.env.NEXUS_ROOT;
|
|
15
|
+
return path.join(process.env.HOME || process.env.USERPROFILE || ".", ".n2n-nexus");
|
|
16
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path Resolution Logic
|
|
3
|
+
*/
|
|
4
|
+
import path from "path";
|
|
5
|
+
import os from "os";
|
|
6
|
+
import { SERVICE_NAME } from "../constants.js";
|
|
7
|
+
import { getArg } from "./cli.js";
|
|
8
|
+
/**
|
|
9
|
+
* Normalize and resolve the root storage path
|
|
10
|
+
*/
|
|
11
|
+
export function normalizeRootPath(inputPath) {
|
|
12
|
+
// Priority: CLI --root > ENV NEXUS_ROOT > System Default
|
|
13
|
+
let root = inputPath || process.env.NEXUS_ROOT || getDefaultDataDir();
|
|
14
|
+
// Resolve ~ to home directory
|
|
15
|
+
if (root.startsWith("~")) {
|
|
16
|
+
root = path.join(os.homedir(), root.slice(1));
|
|
17
|
+
}
|
|
18
|
+
// Cross-platform adaptation (WSL <-> Windows)
|
|
19
|
+
if (process.platform === "linux" && /^[a-zA-Z]:[/\\]/.test(root)) {
|
|
20
|
+
const drive = root[0].toLowerCase();
|
|
21
|
+
root = `/mnt/${drive}${root.slice(2).replace(/\\/g, "/")}`;
|
|
22
|
+
}
|
|
23
|
+
return path.resolve(root);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get the default data directory
|
|
27
|
+
*/
|
|
28
|
+
export function getDefaultDataDir() {
|
|
29
|
+
const home = os.homedir();
|
|
30
|
+
// Use ~/.n2n-nexus for all platforms (developer-friendly convention)
|
|
31
|
+
return path.join(home, `.${SERVICE_NAME}`);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get the root storage path from CLI or environment
|
|
35
|
+
*/
|
|
36
|
+
export function getRootPath() {
|
|
37
|
+
return normalizeRootPath(getArg("--root"));
|
|
38
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nexus Network Constants
|
|
3
|
+
*
|
|
4
|
+
* Centralized configuration for network-related settings.
|
|
5
|
+
*/
|
|
6
|
+
// Service identification
|
|
7
|
+
export const SERVICE_NAME = "n2n-nexus";
|
|
8
|
+
// Host address for binding and connecting.
|
|
9
|
+
// Default to localhost because the daemon exposes unauthenticated local admin APIs.
|
|
10
|
+
// Use "0.0.0.0" only when you intentionally expose it to another trusted environment.
|
|
11
|
+
export const NEXUS_HOST = "127.0.0.1";
|
|
12
|
+
// Port range for auto-election
|
|
13
|
+
export const PORT_RANGE_START = 5688;
|
|
14
|
+
export const PORT_RANGE_END = 5800;
|
|
15
|
+
// Timeouts (milliseconds)
|
|
16
|
+
export const HANDSHAKE_TIMEOUT = 200;
|
|
17
|
+
export const HEARTBEAT_INTERVAL = 30000;
|
|
18
|
+
// Task cleanup
|
|
19
|
+
export const TASK_CLEANUP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
20
|
+
// File I/O
|
|
21
|
+
export const FILE_ENCODING = "utf-8";
|
|
22
|
+
export const PACKAGE_JSON = "package.json";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createDaemonServer } from "./server.js";
|
|
2
|
+
import { pkg, getRootPath } from "../config/index.js";
|
|
3
|
+
import { NEXUS_HOST } from "../constants.js";
|
|
4
|
+
function parsePort() {
|
|
5
|
+
const argIndex = process.argv.indexOf("--port");
|
|
6
|
+
if (argIndex !== -1) {
|
|
7
|
+
const parsed = parseInt(process.argv[argIndex + 1] || "", 10);
|
|
8
|
+
if (!isNaN(parsed) && parsed > 0)
|
|
9
|
+
return parsed;
|
|
10
|
+
}
|
|
11
|
+
const envPort = parseInt(process.env.NEXUS_DAEMON_PORT || process.env.NEXUS_PORT || "", 10);
|
|
12
|
+
if (!isNaN(envPort) && envPort > 0)
|
|
13
|
+
return envPort;
|
|
14
|
+
return 5688;
|
|
15
|
+
}
|
|
16
|
+
function parseHost() {
|
|
17
|
+
const argIndex = process.argv.indexOf("--host");
|
|
18
|
+
if (argIndex !== -1 && process.argv[argIndex + 1]) {
|
|
19
|
+
return process.argv[argIndex + 1];
|
|
20
|
+
}
|
|
21
|
+
return process.env.NEXUS_HOST || NEXUS_HOST;
|
|
22
|
+
}
|
|
23
|
+
export async function runDaemon() {
|
|
24
|
+
const port = parsePort();
|
|
25
|
+
const host = parseHost();
|
|
26
|
+
const root = getRootPath();
|
|
27
|
+
// Inject root path into CONFIG via env so StorageManager picks it up
|
|
28
|
+
process.env.NEXUS_ROOT = root;
|
|
29
|
+
const { server, storageInfo } = await createDaemonServer({ port, host, version: pkg.version });
|
|
30
|
+
await new Promise((resolve, reject) => {
|
|
31
|
+
server.once("error", reject);
|
|
32
|
+
server.listen(port, host, () => {
|
|
33
|
+
console.error(`[n2n-nexus] Daemon v${pkg.version} listening on http://${host}:${port}`);
|
|
34
|
+
console.error(`[n2n-nexus] Storage: ${root} (${storageInfo.storageMode}${storageInfo.isDegraded ? ", degraded" : ""})`);
|
|
35
|
+
resolve();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
process.once("SIGINT", () => { console.error("[n2n-nexus] Shutting down."); server.close(() => process.exit(0)); });
|
|
39
|
+
process.once("SIGTERM", () => { server.close(() => process.exit(0)); });
|
|
40
|
+
console.error("[n2n-nexus] Ready. Press Ctrl+C to stop.");
|
|
41
|
+
}
|