n2n-nexus 0.4.2 → 0.7.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/CHANGELOG.md +114 -0
- package/README.md +77 -117
- package/assets/n2n-nexus-banner.jpg +0 -0
- package/assets/n2n-nexus-icon.png +0 -0
- package/assets/n2n-nexus-logo.png +0 -0
- package/build/client/nexus-client.js +44 -21
- package/build/config/cli.js +0 -3
- package/build/config/index.js +0 -10
- package/build/constants.js +7 -11
- package/build/daemon/index.js +35 -19
- package/build/daemon/rate-limit.js +62 -0
- package/build/daemon/server.js +422 -545
- package/build/daemon/tool-outputs.js +177 -0
- package/build/daemon/tools.js +321 -0
- package/build/index.js +1 -1
- package/build/server/instance-id.js +19 -0
- package/build/server/nexus.js +243 -47
- package/build/storage/direct-messages.js +89 -0
- package/build/storage/docs.js +14 -7
- package/build/storage/events.js +31 -0
- package/build/storage/fs-utils.js +42 -0
- package/build/storage/global-messages.js +44 -0
- package/build/storage/index.js +47 -41
- package/build/storage/migrations.js +86 -0
- package/build/storage/paths.js +0 -3
- package/build/storage/projects.js +4 -5
- package/build/storage/registry.js +67 -25
- package/build/storage/sessions.js +77 -0
- package/build/storage/sqlite-meeting.js +35 -23
- package/build/storage/sqlite.js +73 -24
- package/build/storage/tasks.js +29 -103
- package/build/types.js +1 -1
- package/docs/ARCHITECTURE.md +36 -25
- package/docs/ARCHITECTURE_zh.md +37 -26
- package/docs/ASSISTANT_GUIDE.md +77 -65
- package/docs/README_zh.md +34 -19
- package/llms.txt +12 -2
- package/package.json +24 -14
- package/build/storage/logs.js +0 -60
- package/build/storage/meetings.js +0 -276
- package/build/storage/store.js +0 -153
package/docs/ASSISTANT_GUIDE.md
CHANGED
|
@@ -1,120 +1,132 @@
|
|
|
1
1
|
# Nexus Assistant Guide
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
You are now part of the **n2ns Nexus** coordination network. All capabilities are provided by a standalone **daemon** — every tool you use is defined and executed by the daemon. The MCP adapter is a transparent forwarding layer only.
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> **Prerequisite**: Before calling any tool, confirm that the daemon is running (`n2n-nexus daemon --port 5688`) and that your MCP is connected (a non-empty tool list means you are connected).
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Core principle: discover first, then act
|
|
10
10
|
|
|
11
|
-
### 1.
|
|
11
|
+
### 1. Understand the global system state
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Before starting any task, use these tools to orient yourself:
|
|
14
14
|
|
|
15
15
|
```
|
|
16
|
-
get_global_topology() →
|
|
17
|
-
get_global_topology(projectId) →
|
|
18
|
-
search_projects(query) →
|
|
19
|
-
|
|
16
|
+
get_global_topology() → summary list of all projects + dependency stats
|
|
17
|
+
get_global_topology(projectId) → detailed dependency sub-graph for one project
|
|
18
|
+
search_projects(query) → search projects by name or description
|
|
19
|
+
get_project(projectId) → full manifest + internal docs of one project
|
|
20
|
+
read_messages(count) → fetch messages you have not yet read (incremental, auto-cursor)
|
|
20
21
|
```
|
|
21
22
|
|
|
22
|
-
### 2.
|
|
23
|
+
### 2. Project IDs
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
Project operations need no implicit project context: every project tool takes the `projectId` it acts on.
|
|
26
|
+
Project IDs must follow `[prefix]_[name]` format, for example: `web_my-site`, `api_user-auth`, `mcp_nexus`.
|
|
27
|
+
|
|
28
|
+
### 3. Write project data
|
|
25
29
|
|
|
26
30
|
```
|
|
27
|
-
|
|
31
|
+
sync_project_assets(manifest, internalDocs) → sync full project data
|
|
32
|
+
update_project(projectId, patch) → partial update of manifest fields
|
|
33
|
+
rename_project(oldId, newId) → rename and cascade-update all references
|
|
34
|
+
upload_project_asset(projectId, fileName, base64Content) → upload a binary file to the project store
|
|
28
35
|
```
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
All write tools run synchronously and return their result directly.
|
|
31
38
|
|
|
32
|
-
###
|
|
39
|
+
### Direct conversations
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
upload_project_asset(fileName, base64Content) → 上传二进制文件到项目库
|
|
39
|
-
```
|
|
41
|
+
1. Call `list_sessions` to find the exact destination `sessionId`; use `update_session` to set your name and project association.
|
|
42
|
+
2. Send `send_message({ to: sessionId, message: "Please review this change" })`. Inspect `status`: accepted means stored, refused means rejected.
|
|
43
|
+
3. The recipient calls `read_inbox`, then `acknowledge_message({ messageId })` to confirm receipt or passes `outcome: "refused"` to decline.
|
|
44
|
+
4. Reply with `send_message({ to: original.from, replyTo: original.messageId, message: "Review result..." })`. The sender can query `get_message({ messageId })` for the original receipt.
|
|
40
45
|
|
|
41
|
-
|
|
46
|
+
Unconfirmed fetched messages can appear again; use their IDs to recognize retries. Acknowledgement does not mean work is complete. The inbox is pull-only: there is no automatic model wakeup. Connection restarts create new session IDs; discover the destination again. See [receipt and session contracts](TOOLS_REFERENCE.md#session-directory-and-direct-inboxes).
|
|
42
47
|
|
|
43
|
-
### 4.
|
|
48
|
+
### 4. Messages and collaboration
|
|
44
49
|
|
|
45
50
|
```
|
|
46
|
-
send_message(message, category?)
|
|
47
|
-
read_messages(count?, meetingId?) →
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
send_message(message, category?, meetingId?) → post to meetingId, else the active meeting, else global chat
|
|
52
|
+
read_messages(count?, meetingId?) → read your unread messages (incremental)
|
|
53
|
+
list_events(limit?, action?) → structured activity log (project.sync, meeting.end, ...)
|
|
54
|
+
get_global_strategy() → read the master strategy document
|
|
55
|
+
update_global_strategy(content) → overwrite the master strategy document
|
|
56
|
+
list_global_docs() → list cross-project shared documents
|
|
57
|
+
get_global_doc(docId) → read a shared document
|
|
58
|
+
sync_global_doc(docId, title, content) → create or update a cross-project shared document
|
|
50
59
|
```
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
- `
|
|
55
|
-
- `
|
|
56
|
-
- `
|
|
57
|
-
- `
|
|
61
|
+
Message categories (`category`):
|
|
62
|
+
|
|
63
|
+
- `CHAT` — general discussion (default)
|
|
64
|
+
- `PROPOSAL` — a proposal for others to review
|
|
65
|
+
- `DECISION` — a locked consensus or decision
|
|
66
|
+
- `UPDATE` — a progress update
|
|
67
|
+
- `MEETING_START` — set by the system; do not set manually
|
|
58
68
|
|
|
59
|
-
### 5.
|
|
69
|
+
### 5. Tactical meetings
|
|
60
70
|
|
|
61
71
|
```
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
send_message("...", "
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
list_meetings(status?, limit?) → list meetings with status, participants, counts, summary
|
|
73
|
+
start_meeting(topic) → open a meeting, returns meetingId
|
|
74
|
+
send_message("...", "PROPOSAL") → post a proposal inside the meeting
|
|
75
|
+
send_message("...", "DECISION") → record a decision
|
|
76
|
+
end_meeting(meetingId?, summary?) → close and lock the meeting
|
|
77
|
+
archive_meeting(meetingId) → move to archive (read-only)
|
|
78
|
+
reopen_meeting(meetingId) → reactivate a closed or archived meeting
|
|
68
79
|
```
|
|
69
80
|
|
|
70
|
-
### 6.
|
|
81
|
+
### 6. Task tracking
|
|
71
82
|
|
|
72
83
|
```
|
|
73
|
-
create_task(source_meeting_id?, metadata?) →
|
|
74
|
-
get_task(taskId) →
|
|
75
|
-
list_tasks(status?) →
|
|
76
|
-
cancel_task(taskId) →
|
|
84
|
+
create_task(source_meeting_id?, metadata?) → create a task you manage yourself, returns taskId
|
|
85
|
+
get_task(taskId) → query status and progress
|
|
86
|
+
list_tasks(status?) → list all tasks
|
|
87
|
+
cancel_task(taskId) → cancel a pending or running task
|
|
77
88
|
```
|
|
78
89
|
|
|
79
|
-
### 7.
|
|
90
|
+
### 7. Maintenance
|
|
80
91
|
|
|
81
92
|
```
|
|
82
|
-
host_maintenance(action, count) → prune
|
|
83
|
-
host_delete_project(projectId) →
|
|
93
|
+
host_maintenance(action, count) → prune the oldest N global messages / clear all
|
|
94
|
+
host_delete_project(projectId) → permanently delete a project (irreversible)
|
|
84
95
|
```
|
|
85
96
|
|
|
86
97
|
---
|
|
87
98
|
|
|
88
|
-
##
|
|
99
|
+
## Typical workflows
|
|
100
|
+
|
|
101
|
+
### First connection to the system
|
|
89
102
|
|
|
90
|
-
### 首次接入系统
|
|
91
103
|
```
|
|
92
|
-
1. get_global_topology() ←
|
|
93
|
-
2. read_messages(20) ←
|
|
94
|
-
3.
|
|
104
|
+
1. get_global_topology() ← learn what projects exist
|
|
105
|
+
2. read_messages(20) ← catch up on recent discussion
|
|
106
|
+
3. get_project(projectId) ← read the manifest and docs of the project you work on
|
|
95
107
|
```
|
|
96
108
|
|
|
97
|
-
###
|
|
109
|
+
### Sync project data
|
|
110
|
+
|
|
98
111
|
```
|
|
99
|
-
1.
|
|
100
|
-
2. sync_project_assets(manifest, internalDocs) ← 返回 taskId
|
|
101
|
-
3. get_task(taskId) ← 轮询直到 completed
|
|
112
|
+
1. sync_project_assets(manifest, internalDocs) ← returns { ok, projectId }
|
|
102
113
|
```
|
|
103
114
|
|
|
104
|
-
###
|
|
115
|
+
### Start a collaborative discussion
|
|
116
|
+
|
|
105
117
|
```
|
|
106
118
|
1. start_meeting("Topic")
|
|
107
|
-
2. send_message("
|
|
108
|
-
3. read_messages() ←
|
|
109
|
-
4. send_message("
|
|
119
|
+
2. send_message("My proposal...", "PROPOSAL")
|
|
120
|
+
3. read_messages() ← read replies from other instances
|
|
121
|
+
4. send_message("Consensus reached", "DECISION")
|
|
110
122
|
5. end_meeting(meetingId, "summary")
|
|
111
123
|
```
|
|
112
124
|
|
|
113
125
|
---
|
|
114
126
|
|
|
115
|
-
##
|
|
127
|
+
## Notes
|
|
116
128
|
|
|
117
|
-
- `read_messages`
|
|
118
|
-
-
|
|
119
|
-
-
|
|
120
|
-
- daemon
|
|
129
|
+
- `read_messages` is **incremental**: each call returns only messages received since the last read. The cursor is managed automatically by the daemon — do not pass `afterId`.
|
|
130
|
+
- Write tools (`sync_project_assets`, `update_project`, `rename_project`, `host_delete_project`) complete before they return; there is nothing to poll.
|
|
131
|
+
- When a project ID is referenced by other projects via `relations`, renaming it automatically cascades to all references — no manual cleanup needed.
|
|
132
|
+
- After a daemon restart, your MCP reconnects automatically and restores the tool list. You do not need to restart the IDE.
|
package/docs/README_zh.md
CHANGED
|
@@ -31,18 +31,18 @@ n2n-nexus 是一个开源的 Model Context Protocol (MCP) 协同服务器,适
|
|
|
31
31
|
- AI 助手项目注册表
|
|
32
32
|
- 编码 agent 会议和任务协同
|
|
33
33
|
|
|
34
|
-
n2n-nexus 面向这些场景设计:共享 daemon
|
|
34
|
+
n2n-nexus 面向这些场景设计:共享 daemon 状态、轻量 MCP 适配器、项目感知协作、本地存储和跨环境 IDE 工作流。
|
|
35
35
|
|
|
36
36
|
## n2n-nexus 是什么?
|
|
37
37
|
|
|
38
38
|
n2n-nexus 为 AI 助手提供共享协作空间。不同助手不再只依赖自己的聊天上下文,而是可以通过 MCP 读取和更新同一个本地 daemon 中的项目状态。
|
|
39
39
|
|
|
40
|
-
**
|
|
40
|
+
**Quick summary**
|
|
41
41
|
|
|
42
42
|
- **安装**:`npx n2n-nexus daemon --port 5688`
|
|
43
43
|
- **协议**:Model Context Protocol (MCP),适配器通过 HTTP 连接 daemon
|
|
44
44
|
- **存储**:默认在 `~/.n2n-nexus` 下使用本地文件和 SQLite
|
|
45
|
-
-
|
|
45
|
+
- **适合**:多助手编码会话、项目交接、共享决策、会议记录、任务跟踪、项目 Manifest
|
|
46
46
|
- **不适合**:云端团队聊天、公开项目管理 SaaS、源码索引、向量搜索或远程数据库托管
|
|
47
47
|
|
|
48
48
|
## 为什么使用它?
|
|
@@ -66,15 +66,17 @@ n2n-nexus 为 AI 助手提供共享协作空间。不同助手不再只依赖自
|
|
|
66
66
|
▼ ▼ ▼
|
|
67
67
|
MCP-A MCP-B MCP-C
|
|
68
68
|
(Win) (WSL) (SSH/VM)
|
|
69
|
-
|
|
69
|
+
每条 MCP 连接一个轻量适配器
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
- **Daemon 是唯一数据源**:启动一次并保持运行。
|
|
73
|
-
- **MCP
|
|
73
|
+
- **MCP 适配器只维护连接身份**:每个 IDE 通过 `npx` 启动适配器,然后把工具调用转发给 daemon;会话目录和收件箱保存在 daemon。
|
|
74
74
|
- **跨环境协作**:通过 `NEXUS_ENDPOINT` 让不同机器或 shell 指向同一个 daemon。daemon 默认只监听本机;只有在可信网络中才使用 `--host 0.0.0.0`。
|
|
75
75
|
|
|
76
76
|
## 快速开始
|
|
77
77
|
|
|
78
|
+
运行环境要求 Node.js 22.x 中的 22.14.0 或更高版本,或 Node.js 23.6.0 及以上版本。
|
|
79
|
+
|
|
78
80
|
### 1. 启动 daemon
|
|
79
81
|
|
|
80
82
|
```bash
|
|
@@ -110,43 +112,50 @@ npx n2n-nexus daemon --port 5688
|
|
|
110
112
|
|
|
111
113
|
## 工具集
|
|
112
114
|
|
|
113
|
-
### 会话和上下文
|
|
114
|
-
|
|
115
|
-
- `register_session_context`:声明当前活跃项目 ID。
|
|
116
|
-
|
|
117
115
|
### 项目资产管理
|
|
118
116
|
|
|
119
117
|
- `sync_project_assets`:提交项目 Manifest 和内部文档。
|
|
120
118
|
- `update_project`:局部更新项目 Manifest。
|
|
121
119
|
- `rename_project`:重命名项目 ID 并更新关联关系。
|
|
122
|
-
- `upload_project_asset
|
|
120
|
+
- `upload_project_asset`:向指定项目上传二进制或文本资产。
|
|
123
121
|
- `search_projects`:搜索项目注册表。
|
|
124
122
|
- `get_global_topology`:查看项目拓扑和依赖关系。
|
|
123
|
+
- `get_project`:读取项目完整 Manifest 和内部文档。
|
|
125
124
|
|
|
126
125
|
### 消息和协作
|
|
127
126
|
|
|
128
|
-
- `send_message
|
|
129
|
-
- `
|
|
127
|
+
- `send_message`:向精确会话 ID 发送定向消息,也保留会议消息和全局消息。
|
|
128
|
+
- `list_sessions` / `update_session`:发现连接,设置自己的名称、项目关联和接收策略。
|
|
129
|
+
- `read_inbox` / `acknowledge_message`:取回个人消息并明确确认或拒绝。
|
|
130
|
+
- `get_message`:查询自己发送或接收的消息状态和回复关联。
|
|
131
|
+
- `read_messages`:增量读取未读消息(全局或指定会议)。
|
|
132
|
+
- `list_events`:读取结构化活动日志(项目同步、重命名、会议结束等)。
|
|
133
|
+
- `get_global_strategy`:读取主策略文档。
|
|
130
134
|
- `update_global_strategy`:更新主策略文档。
|
|
135
|
+
- `list_global_docs`:列出共享文档。
|
|
136
|
+
- `get_global_doc`:读取共享文档。
|
|
131
137
|
- `sync_global_doc`:创建或更新共享文档。
|
|
132
138
|
|
|
139
|
+
每条 MCP 连接有独立会话 ID;多个聊天若共用连接,仍视为一个会话。在线仅表示近期心跳,消息采用主动拉取,不自动唤醒模型。已存储、已取回、已确认和已回复是不同状态,确认收件不等于任务完成。使用流程见[助手指南](ASSISTANT_GUIDE_zh.md),精确契约见[工具参考](TOOLS_REFERENCE.md#session-directory-and-direct-inboxes)。
|
|
140
|
+
|
|
133
141
|
### 会议管理
|
|
134
142
|
|
|
143
|
+
- `list_meetings`:列出会议(状态、参与者、消息与决策数、摘要)。
|
|
135
144
|
- `start_meeting`:开启会议会话。
|
|
136
145
|
- `end_meeting`:关闭并锁定会议。
|
|
137
146
|
- `archive_meeting`:归档已关闭会议。
|
|
138
147
|
- `reopen_meeting`:重新打开已关闭或已归档会议。
|
|
139
148
|
|
|
140
|
-
###
|
|
149
|
+
### 任务管理
|
|
141
150
|
|
|
142
|
-
- `create_task
|
|
151
|
+
- `create_task`:创建由助手自行管理的任务。
|
|
143
152
|
- `get_task`:查询任务状态和结果。
|
|
144
153
|
- `list_tasks`:按状态列出任务。
|
|
145
154
|
- `cancel_task`:取消等待中或运行中的任务。
|
|
146
155
|
|
|
147
156
|
### 维护
|
|
148
157
|
|
|
149
|
-
- `host_maintenance
|
|
158
|
+
- `host_maintenance`:清理或清空全局聊天流。
|
|
150
159
|
- `host_delete_project`:删除项目和资产。
|
|
151
160
|
|
|
152
161
|
## 数据存储
|
|
@@ -209,7 +218,11 @@ NEXUS_ENDPOINT=http://127.0.0.1:5688 n2n-nexus mcp
|
|
|
209
218
|
| `NEXUS_ENDPOINT` | MCP 适配器连接的 daemon 地址 | `http://127.0.0.1:5688` |
|
|
210
219
|
| `NEXUS_ROOT` | daemon 存储根目录 | `~/.n2n-nexus` |
|
|
211
220
|
| `NEXUS_HOST` | daemon 监听地址 | `127.0.0.1` |
|
|
212
|
-
| `
|
|
221
|
+
| `NEXUS_DAEMON_PORT` | daemon 端口(`--port` 优先) | `5688` |
|
|
222
|
+
| `NEXUS_RATE_LIMIT` | 每个实例每秒工具调用数,每个客户端地址为其 5 倍;`0` 关闭限流 | `20` |
|
|
223
|
+
| `NEXUS_RATE_BURST` | 每个实例可一次性突发的调用数(最小为 1) | `60` |
|
|
224
|
+
| `NEXUS_ALLOWED_HOSTS` | `Host` 头额外允许的域名,逗号分隔(IP、`localhost`、`host.docker.internal` 始终允许) | 空 |
|
|
225
|
+
| `NEXUS_INSTANCE_ID` | 覆盖 MCP 实例 ID(未读游标、会议权限都以它为键) | `mcp-<主机名>-<工作目录哈希>-<IDE 名>` |
|
|
213
226
|
|
|
214
227
|
## 安全和治理说明
|
|
215
228
|
|
|
@@ -226,10 +239,12 @@ NEXUS_ENDPOINT=http://127.0.0.1:5688 n2n-nexus mcp
|
|
|
226
239
|
| 文件 | 说明 |
|
|
227
240
|
| --- | --- |
|
|
228
241
|
| [会议纪要](./MEETING_MINUTES_2025-12-29.md) | 结构化决策和测试记录 |
|
|
229
|
-
| [讨论日志](./discussion_2025-12-
|
|
242
|
+
| [讨论日志](./discussion_2025-12-29.md) | 可读的讨论记录 |
|
|
230
243
|
|
|
231
244
|
## 本地开发
|
|
232
245
|
|
|
246
|
+
开发请使用 Node.js 24,与 CI 和 lint 工具链保持一致。
|
|
247
|
+
|
|
233
248
|
```bash
|
|
234
249
|
git clone https://github.com/n2ns/n2n-nexus.git
|
|
235
250
|
cd n2n-nexus
|
|
@@ -272,7 +287,7 @@ NEXUS_ENDPOINT=http://127.0.0.1:5688 node build/index.js mcp
|
|
|
272
287
|
## 相关文档
|
|
273
288
|
|
|
274
289
|
- [架构文档](./ARCHITECTURE_zh.md)
|
|
275
|
-
- [AI 助手指南](./
|
|
290
|
+
- [AI 助手指南](./ASSISTANT_GUIDE_zh.md)
|
|
276
291
|
- [英文 README](../README.md)
|
|
277
292
|
- [llms.txt](../llms.txt)
|
|
278
293
|
|
|
@@ -282,4 +297,4 @@ NEXUS_ENDPOINT=http://127.0.0.1:5688 node build/index.js mcp
|
|
|
282
297
|
|
|
283
298
|
---
|
|
284
299
|
|
|
285
|
-
Built by N2NS Lab,
|
|
300
|
+
Built by N2NS Lab, Datafrog's open-source lab for practical AI developer tools.
|
package/llms.txt
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# n2n-nexus
|
|
2
2
|
|
|
3
|
-
n2n-nexus is a local-first Model Context Protocol (MCP) coordination hub from N2NS Lab for multi-AI assistant collaboration. It uses a long-running local daemon as the source of truth and
|
|
3
|
+
n2n-nexus is a local-first Model Context Protocol (MCP) coordination hub from N2NS Lab for multi-AI assistant collaboration. It uses a long-running local daemon as the source of truth and lightweight MCP adapters for IDEs and AI coding assistants. Each MCP connection has a discoverable session identity and a pull-based direct inbox with explicit receipts and reply links; messages do not automatically wake models.
|
|
4
|
+
|
|
5
|
+
## Search intent coverage
|
|
6
|
+
|
|
7
|
+
- multi-agent MCP coordination
|
|
8
|
+
- multi-AI assistant collaboration
|
|
9
|
+
- local MCP coordination hub
|
|
10
|
+
- shared project context for AI coding agents
|
|
11
|
+
- cross-IDE MCP collaboration server
|
|
12
|
+
- local-first project registry for AI assistants
|
|
13
|
+
- meeting and task coordination for coding agents
|
|
4
14
|
|
|
5
15
|
## Core positioning
|
|
6
16
|
|
|
@@ -43,4 +53,4 @@ Default storage root is `~/.n2n-nexus`, containing global docs, project manifest
|
|
|
43
53
|
|
|
44
54
|
## Brand
|
|
45
55
|
|
|
46
|
-
Built by N2NS Lab,
|
|
56
|
+
Built by N2NS Lab, Datafrog's open-source lab for practical AI developer tools.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n2n-nexus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Local-first MCP coordination hub from N2NS Lab for multi-AI assistant collaboration.",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"build",
|
|
12
|
+
"assets",
|
|
12
13
|
"README.md",
|
|
13
14
|
"llms.txt",
|
|
14
15
|
"CHANGELOG.md",
|
|
@@ -49,6 +50,10 @@
|
|
|
49
50
|
"ai-developer-tools"
|
|
50
51
|
],
|
|
51
52
|
"author": "N2NS Lab",
|
|
53
|
+
"funding": {
|
|
54
|
+
"type": "github",
|
|
55
|
+
"url": "https://github.com/sponsors/n2ns"
|
|
56
|
+
},
|
|
52
57
|
"license": "Apache-2.0",
|
|
53
58
|
"repository": {
|
|
54
59
|
"type": "git",
|
|
@@ -59,32 +64,37 @@
|
|
|
59
64
|
"url": "https://github.com/n2ns/n2n-nexus/issues"
|
|
60
65
|
},
|
|
61
66
|
"engines": {
|
|
62
|
-
"node": "
|
|
67
|
+
"node": "^22.14.0 || >=23.6.0"
|
|
63
68
|
},
|
|
64
69
|
"scripts": {
|
|
65
|
-
"build": "tsc",
|
|
70
|
+
"build": "node -e \"fs.rmSync('build', { recursive: true, force: true })\" && tsc",
|
|
66
71
|
"start": "node build/index.js",
|
|
67
72
|
"daemon": "node build/index.js daemon",
|
|
68
73
|
"dev": "tsc -w",
|
|
69
74
|
"test": "vitest run",
|
|
75
|
+
"audit": "npm audit",
|
|
76
|
+
"audit:prod": "npm audit --omit=dev",
|
|
77
|
+
"pack:dry-run": "npm pack --dry-run",
|
|
78
|
+
"check": "npm run lint && npm test && npm run build && npm run audit && npm run pack:dry-run",
|
|
70
79
|
"test:publish": "npm run build && npm test && npm pack --dry-run",
|
|
71
80
|
"test:integration": "vitest run tests/daemon_projects.test.ts tests/daemon_messages.test.ts tests/message_cursor.test.ts",
|
|
72
|
-
"lint": "eslint src
|
|
73
|
-
"prepublishOnly": "npm run
|
|
81
|
+
"lint": "eslint src tests",
|
|
82
|
+
"prepublishOnly": "npm run check"
|
|
74
83
|
},
|
|
75
84
|
"dependencies": {
|
|
76
|
-
"@modelcontextprotocol/
|
|
77
|
-
"better-sqlite3": "^
|
|
78
|
-
"zod": "^4.
|
|
85
|
+
"@modelcontextprotocol/server": "^2.3.1",
|
|
86
|
+
"better-sqlite3": "^13.0.3",
|
|
87
|
+
"zod": "^4.6.7"
|
|
79
88
|
},
|
|
80
89
|
"devDependencies": {
|
|
81
90
|
"@eslint/js": "^10.0.1",
|
|
82
|
-
"@
|
|
83
|
-
"@types/
|
|
84
|
-
"
|
|
85
|
-
"
|
|
91
|
+
"@modelcontextprotocol/client": "^2.3.1",
|
|
92
|
+
"@types/better-sqlite3": "^9.6.0",
|
|
93
|
+
"@types/node": "^26.6.5",
|
|
94
|
+
"eslint": "^10.13.0",
|
|
95
|
+
"lefthook": "^2.2.2",
|
|
86
96
|
"typescript": "^6.0.3",
|
|
87
|
-
"typescript-eslint": "^8.
|
|
88
|
-
"vitest": "^
|
|
97
|
+
"typescript-eslint": "^8.72.0",
|
|
98
|
+
"vitest": "^5.0.5"
|
|
89
99
|
}
|
|
90
100
|
}
|
package/build/storage/logs.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from "node:fs";
|
|
2
|
-
import { NexusPaths } from "./paths.js";
|
|
3
|
-
export class LogStorage {
|
|
4
|
-
static async ensureLogFile() {
|
|
5
|
-
try {
|
|
6
|
-
await fs.access(NexusPaths.globalDiscussion);
|
|
7
|
-
}
|
|
8
|
-
catch {
|
|
9
|
-
await fs.writeFile(NexusPaths.globalDiscussion, "[]", "utf-8");
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
static async addLog(from, text, category) {
|
|
13
|
-
await this.ensureLogFile();
|
|
14
|
-
const logs = await this.getLogs();
|
|
15
|
-
const payload = {
|
|
16
|
-
timestamp: new Date().toISOString(),
|
|
17
|
-
from,
|
|
18
|
-
text,
|
|
19
|
-
category: this.normalizeCategory(category)
|
|
20
|
-
};
|
|
21
|
-
logs.push(payload);
|
|
22
|
-
await fs.writeFile(NexusPaths.globalDiscussion, JSON.stringify(logs, null, 2), "utf-8");
|
|
23
|
-
}
|
|
24
|
-
static normalizeCategory(category) {
|
|
25
|
-
if (!category)
|
|
26
|
-
return undefined;
|
|
27
|
-
const allowed = ["MEETING_START", "PROPOSAL", "DECISION", "UPDATE", "CHAT", "message", "SYSTEM"];
|
|
28
|
-
return allowed.includes(category) ? category : "UPDATE";
|
|
29
|
-
}
|
|
30
|
-
static async getLogs(limit = 10) {
|
|
31
|
-
await this.ensureLogFile();
|
|
32
|
-
const rows = await this.readRawLogs();
|
|
33
|
-
if (limit <= 0)
|
|
34
|
-
return rows;
|
|
35
|
-
return rows.slice(-limit);
|
|
36
|
-
}
|
|
37
|
-
static async pruneLogs(count) {
|
|
38
|
-
const current = await this.readRawLogs();
|
|
39
|
-
if (count <= 0)
|
|
40
|
-
return;
|
|
41
|
-
if (current.length <= count)
|
|
42
|
-
return;
|
|
43
|
-
const remaining = current.slice(count);
|
|
44
|
-
await fs.writeFile(NexusPaths.globalDiscussion, JSON.stringify(remaining, null, 2), "utf-8");
|
|
45
|
-
}
|
|
46
|
-
static async clearLogs() {
|
|
47
|
-
await fs.writeFile(NexusPaths.globalDiscussion, "[]", "utf-8");
|
|
48
|
-
}
|
|
49
|
-
static async readRawLogs() {
|
|
50
|
-
await this.ensureLogFile();
|
|
51
|
-
try {
|
|
52
|
-
const raw = await fs.readFile(NexusPaths.globalDiscussion, "utf-8");
|
|
53
|
-
const parsed = JSON.parse(raw);
|
|
54
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
return [];
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|