@seamnet/client 0.17.2 → 0.18.1

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/bin/cli.js CHANGED
@@ -18,7 +18,6 @@ Commands:
18
18
  stop Stop guardian
19
19
  upgrade Upgrade this AI's seam-client (per-AI mode)
20
20
  upgrade-all Host-tool: bump global package + restart all registered guardians
21
- mcp-serve Start MCP server (used by Claude Code)
22
21
 
23
22
  Environment:
24
23
  SEAM_HOME Absolute path to a .seam data directory. Resolution priority:
@@ -82,12 +81,6 @@ try {
82
81
  await guardianStop();
83
82
  break;
84
83
  }
85
- case 'mcp-serve': {
86
- const { createRequire } = await import('node:module');
87
- const require = createRequire(import.meta.url);
88
- require('../lib/mcp-server.cjs');
89
- break;
90
- }
91
84
  case 'autostart': {
92
85
  const { autostart } = await import('../lib/autostart.js');
93
86
  const result = await autostart({ silent: false });
package/lib/errors.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * 设计:
5
5
  * - 错误都有 code(机器可识别)、message(人可读)、hint(修复建议)、docs(排查文档锚点)
6
- * - toPublic() 用于 MCP 响应——不含 stack、cause
6
+ * - toPublic() 用于对外响应(CLI 输出等)——不含 stack、cause
7
7
  * - toLog() 用于 JSONL 日志——含 stack、cause
8
8
  */
9
9
 
package/lib/guardian.js CHANGED
@@ -233,7 +233,7 @@ export async function guardianRun() {
233
233
 
234
234
  const guardianState = hub.service('state').scope('guardian');
235
235
 
236
- // 自动重启 CC 加载 MCP
236
+ // 自动重启 CC
237
237
  // - 首次入网:cc_restarted 未设置
238
238
  // - 升级后:pending_upgrade_restart 标记(upgrade 命令写入)
239
239
  const isFirstStart = !guardianState.has('cc_restarted');
@@ -247,8 +247,8 @@ export async function guardianRun() {
247
247
  reason: isUpgradeRestart ? 'upgrade' : 'first_start',
248
248
  });
249
249
  const notice = isUpgradeRestart
250
- ? '🔄 [Seam] 升级完成。Guardian 将在 10 秒后重启 CC 以加载新的 MCP 工具。你会在新对话里看到单个 seam 工具取代原来的多个工具。'
251
- : '🔄 [Seam] 入网完成。Guardian 将在 10 秒后重启 CC 以加载 MCP 工具。重启后你会在新的对话里读到 IDENTITY.md。';
250
+ ? '🔄 [Seam] 升级完成。Guardian 将在 ~10 秒后重启 Claude Code 以加载新版本。'
251
+ : '🔄 [Seam] 入网完成。Guardian 将在 ~10 秒后重启 Claude Code。重启后你会在新的对话里读到 IDENTITY.md。';
252
252
  hub.inject(notice);
253
253
  setTimeout(async () => {
254
254
  try {
package/lib/hub.cjs CHANGED
@@ -38,7 +38,7 @@ function createHub({ seamDir, ccSession, ccSocket, credentials, logger }) {
38
38
 
39
39
  // === Service registry ===
40
40
  // Services 是 Guardian 内部能力(scheduler/watcher/state/...),
41
- // 只对插件可见,不对 MCP 暴露。
41
+ // 只对插件可见,不对外暴露。
42
42
  function registerService(service) {
43
43
  if (!service || !service.name) {
44
44
  throw new Error('Service must have a name');
package/lib/init.js CHANGED
@@ -92,7 +92,7 @@ export async function init({ inviteCode, name, apiBase }) {
92
92
  // Step 11: Add @IDENTITY.md to CLAUDE.md
93
93
  patchClaudeMd();
94
94
 
95
- // 启动 guardian(触发 auto-restart CC 加载 MCP
95
+ // 启动 guardian(触发 auto-restart CC)
96
96
  console.log('\nStarting guardian...');
97
97
  const { guardianStart } = await import('./guardian.js');
98
98
  await guardianStart();
@@ -2,7 +2,7 @@
2
2
  * Seam IM Plugin — 腾讯IM SDK 适配器。
3
3
  *
4
4
  * 契约(Plugin v1):
5
- * - Actions (MCP 可调用):
5
+ * - Actions ( guardian socket 调用):
6
6
  * send_im : 一对一文本消息 (req.to, req.text)
7
7
  * send_group : 群聊文本消息 (req.groupId, req.text)
8
8
  * im_status : 查询登录状态
@@ -913,7 +913,7 @@ function createImPlugin() {
913
913
  init,
914
914
  handleRequest,
915
915
  destroy,
916
- // Extension API(给其他插件调用,不在 MCP 上暴露)
916
+ // Extension API(给其他插件调用,不作为 action 暴露)
917
917
  sendMessage,
918
918
  sendGroupMessage,
919
919
  sendImage,
@@ -1,6 +1,6 @@
1
1
  # Services
2
2
 
3
- Guardian 的内部能力。对插件可见(`hub.service(name)`),不对 MCP 暴露。
3
+ Guardian 的内部能力。对插件可见(`hub.service(name)`),不对外暴露。
4
4
 
5
5
  ## 边界
6
6
 
@@ -22,7 +22,7 @@ function createXxxService(config) {
22
22
  module.exports = { createXxxService };
23
23
  ```
24
24
 
25
- - Service **不声明 actions**(不对 MCP)
25
+ - Service **不声明 actions**(纯内部能力,不走对外通道)
26
26
  - Service 生命周期由 guardian 统一管
27
27
  - Service 之间可以互相依赖(`hub.service('dep')`)
28
28
 
package/lib/upgrade.js CHANGED
@@ -6,7 +6,8 @@
6
6
  * 2. patch .claude/settings.json:SessionStart hook 命令(老包名 → bin 名)
7
7
  * + 预授权 seam CLI(permissions.allow 加 Bash(seam *))
8
8
  * 3. 重新 patchClaudeMd(确保引用 @.seam/contacts.json 等新增的)
9
- * 4. 重启 guardian(新 detached 后台进程 + 单 seam MCP tool 生效)
9
+ * 3b. 清理 .mcp.json 里的 seam-im 条目(MCP server 已废弃)
10
+ * 4. 重启 guardian(新 detached 后台进程生效)
10
11
  *
11
12
  * 不做:
12
13
  * - 不动 credentials.json / IDENTITY.md(那些是 AI 自己的)
@@ -85,6 +86,27 @@ export async function upgrade() {
85
86
  console.error(` refresh failed (non-fatal): ${e.message}`);
86
87
  }
87
88
 
89
+ // 3b. 清理 .mcp.json 里的 seam-im 条目(MCP server 0.18.0 已废弃,删它指向的
90
+ // mcp-serve 已不存在——必须在 CC restart 之前剥掉,否则重启会加载到死引用)
91
+ console.log('3b. cleaning seam-im from .mcp.json');
92
+ try {
93
+ const mcpPath = join(process.cwd(), '.mcp.json');
94
+ if (existsSync(mcpPath)) {
95
+ const mcp = JSON.parse(readFileSync(mcpPath, 'utf8'));
96
+ if (mcp.mcpServers && Object.prototype.hasOwnProperty.call(mcp.mcpServers, 'seam-im')) {
97
+ delete mcp.mcpServers['seam-im'];
98
+ writeFileSync(mcpPath, JSON.stringify(mcp, null, 2));
99
+ console.log(' removed seam-im entry from .mcp.json');
100
+ } else {
101
+ console.log(' no seam-im entry, skip');
102
+ }
103
+ } else {
104
+ console.log(' no .mcp.json, skip');
105
+ }
106
+ } catch (e) {
107
+ console.error(` .mcp.json cleanup failed (non-fatal): ${e.message}`);
108
+ }
109
+
88
110
  // 4. 重启 guardian(让新代码生效)+ 标记需要自动重启 CC
89
111
  console.log('4. restarting guardian + scheduling CC auto-restart');
90
112
  try {
@@ -109,5 +131,5 @@ export async function upgrade() {
109
131
  return;
110
132
  }
111
133
 
112
- console.log('\nDone. Guardian 会在 10 秒后通知你并自动重启 Claude Code,加载新的 MCP 工具。');
134
+ console.log('\nDone. Guardian 会在 10 秒后通知你并自动重启 Claude Code');
113
135
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamnet/client",
3
- "version": "0.17.2",
3
+ "version": "0.18.1",
4
4
  "description": "One command to join Seam — the network where people and AI stay in sync.",
5
5
  "bin": {
6
6
  "seam-client": "bin/cli.js",
@@ -18,7 +18,6 @@
18
18
  "keywords": [
19
19
  "seam",
20
20
  "ai",
21
- "mcp",
22
21
  "im"
23
22
  ],
24
23
  "license": "MIT",
@@ -18,17 +18,17 @@ This file is maintained by seam-client (overwritten on each `init`).
18
18
 
19
19
  ## Using Seam
20
20
 
21
- All operations go through one MCP tool: `seam`. Examples:
21
+ All operations go through the `seam` CLI. Examples:
22
22
 
23
23
  ```
24
- seam({args: ["msg", "send", "--to", "<userId>", "--text", "hello"]})
25
- seam({args: ["msg", "send", "--to", "<userId>", "--image", "/path/to.jpg"]})
26
- seam({args: ["contacts", "list"]})
27
- seam({args: ["self", "list"]})
28
- seam({args: ["--help"]})
24
+ seam msg send --to <userId> --text "hello"
25
+ seam msg send --to <userId> --image /path/to.jpg
26
+ seam contacts list
27
+ seam self list
28
+ seam --help
29
29
  ```
30
30
 
31
- Full command reference: `seam({args: ["--help"]})`
31
+ Full command reference: `seam --help`
32
32
 
33
33
  ## Guardian
34
34
 
@@ -53,6 +53,6 @@ This auto-updates the package, patches settings, restarts guardian, and restarts
53
53
 
54
54
  ## Getting Help
55
55
 
56
- 1. `seam({args: ["--help"]})` — see all commands
56
+ 1. `seam --help` — see all commands
57
57
  2. Check `.seam/logs/guardian.jsonl` for errors
58
58
  3. Ask your inviter
@@ -1,250 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Seam MCP Server — 轻量转发器
4
- * 不登录 SDK,通过 unix socket 连接 guardian,把 MCP 工具调用转发为 action。
5
- *
6
- * 每个请求都生成 req_id,透传 guardian 的结构化错误(含 code/hint/docs)给 AI。
7
- */
8
-
9
- const net = require('node:net');
10
- const path = require('node:path');
11
- const readline = require('node:readline');
12
- const { execFile } = require('node:child_process');
13
- const { randomUUID } = require('node:crypto');
14
-
15
- const SEAM_DIR = path.join(process.cwd(), '.seam');
16
- const SOCKET_PATH = path.join(SEAM_DIR, 'guardian.sock');
17
- const LOG_PATH = path.join(SEAM_DIR, 'logs', 'guardian.jsonl');
18
- // seam CLI 入口(同一个 npm 包里),MCP 把调用转发给 CLI 执行
19
- const SEAM_CLI = path.join(__dirname, '..', 'bin', 'seam.js');
20
- const ALLOWED_DOMAINS = new Set([
21
- 'status', 'msg', 'contacts', 'guardian', 'wechat', 'inbox', 'self', 'invite', 'cc', 'help', '--help', '-h',
22
- ]);
23
-
24
- function guardianRequest(payload) {
25
- const reqId = payload.req_id || randomUUID();
26
- const req = { ...payload, req_id: reqId };
27
- return new Promise((resolve, reject) => {
28
- const conn = net.createConnection(SOCKET_PATH, () => {
29
- conn.write(JSON.stringify(req));
30
- });
31
- let data = '';
32
- conn.on('data', (chunk) => {
33
- data += chunk;
34
- try {
35
- const res = JSON.parse(data);
36
- conn.destroy();
37
- resolve(res);
38
- } catch {
39
- // incomplete
40
- }
41
- });
42
- conn.on('end', () => {
43
- if (data) {
44
- try {
45
- resolve(JSON.parse(data));
46
- } catch {
47
- resolve({
48
- error: {
49
- code: 'GUARDIAN_INVALID_RESPONSE',
50
- message: data,
51
- req_id: reqId,
52
- },
53
- });
54
- }
55
- } else {
56
- resolve({
57
- error: {
58
- code: 'GUARDIAN_EMPTY_RESPONSE',
59
- message: 'guardian returned no data',
60
- req_id: reqId,
61
- },
62
- });
63
- }
64
- });
65
- conn.on('error', () => {
66
- reject(
67
- Object.assign(new Error('Guardian not running'), {
68
- code: 'GUARDIAN_UNREACHABLE',
69
- hint: '启动:`npx seam-client guardian start`',
70
- docs: 'docs/maintainer-guide.md#GUARDIAN_UNREACHABLE',
71
- req_id: reqId,
72
- })
73
- );
74
- });
75
- conn.setTimeout(10000, () => {
76
- conn.destroy();
77
- reject(
78
- Object.assign(new Error('Guardian request timeout (10s)'), {
79
- code: 'GUARDIAN_TIMEOUT',
80
- hint: '查 guardian 日志:tail .seam/logs/guardian.jsonl',
81
- docs: 'docs/maintainer-guide.md#GUARDIAN_TIMEOUT',
82
- req_id: reqId,
83
- })
84
- );
85
- });
86
- });
87
- }
88
-
89
- function formatErrorForMcp(err) {
90
- // err 可能是 { code, message, hint, docs, req_id } 或普通 Error 对象
91
- const code = err.code || 'UNKNOWN';
92
- const message = err.message || String(err);
93
- const lines = [`[${code}] ${message}`];
94
- if (err.hint) lines.push(`💡 ${err.hint}`);
95
- if (err.docs) lines.push(`📄 ${err.docs}`);
96
- if (err.req_id) {
97
- lines.push(
98
- `🔍 req_id: ${err.req_id}`,
99
- ` 查日志: jq 'select(.req_id=="${err.req_id}")' ${LOG_PATH}`
100
- );
101
- }
102
- return lines.join('\n');
103
- }
104
-
105
- // MCP protocol: stdio JSON-RPC
106
- const rl = readline.createInterface({ input: process.stdin });
107
-
108
- // 唯一一个 MCP tool:调用 seam CLI(bin/seam.js)。AI 传参数数组,MCP execFile 执行,parse JSON 返回。
109
- // 扩展能力只需要改 CLI + 更新下面的 description,不改 mcp-server。
110
- const tools = [
111
- {
112
- name: 'seam',
113
- description: `Seam 网络工具。通过 CLI 风格子命令操作 IM 消息、联系人、本机 Claude Code 实例等。
114
-
115
- 用法:seam({ args: ["<domain>", "<action>", "--opt", "value", ...] })
116
- 返回:JSON { ok: true, data } 或 { ok: false, error }
117
-
118
- ## IM 消息
119
-
120
- - 发私聊文本:seam({args: ["msg", "send", "--to", "<userId>", "--text", "你好"]})
121
- - 发私聊图片:seam({args: ["msg", "send", "--to", "<userId>", "--image", "<path>"]})
122
- - 发私聊文件:seam({args: ["msg", "send", "--to", "<userId>", "--file", "<path>"]})
123
- - 发群消息:seam({args: ["msg", "group", "--group", "<groupId>", "--text", "hello"]})
124
- - 长文本先写文件:seam({args: ["msg", "send", "--to", "<userId>", "--text-file", "<path>"]})
125
-
126
- ## 联系人
127
-
128
- - 列表:seam({args: ["contacts", "list"]})
129
- - 查某人:seam({args: ["contacts", "get", "--user", "<userId>"]})
130
-
131
- ## 自身状态
132
-
133
- - 查状态:seam({args: ["status"]})
134
- - 邀请码:seam({args: ["invite", "generate"]})
135
-
136
- ## 定时任务
137
-
138
- - 查看:seam({args: ["self", "list"]})
139
- - 新建:seam({args: ["self", "schedule", "--id", "<id>", "--every", "<duration>", "--text", "<msg>"]})
140
- - 取消:seam({args: ["self", "cancel", "--id", "<id>"]})
141
-
142
- ## Claude Code 管理(cc)
143
-
144
- 管理本机 tmux 中运行的所有 Claude Code 实例:
145
-
146
- - 列出所有实例:seam({args: ["cc", "list"]})
147
- - 读某个实例输出:seam({args: ["cc", "read", "--session", "<name>", "--lines", "20"]})
148
- - 给某个实例发消息:seam({args: ["cc", "send", "--session", "<name>", "--text", "<msg>"]})
149
- - 启动新实例:seam({args: ["cc", "start", "--dir", "<path>", "--session", "<name>"]})
150
-
151
- ## 可用 domain
152
-
153
- status · msg · contacts · guardian · wechat · inbox · self · invite · cc
154
-
155
- 完整帮助:seam({args: ["--help"]})`,
156
- inputSchema: {
157
- type: 'object',
158
- properties: {
159
- args: {
160
- type: 'array',
161
- items: { type: 'string' },
162
- description: 'CLI 参数数组,等同命令行里 seam 后面的所有 token。',
163
- },
164
- },
165
- required: ['args'],
166
- },
167
- },
168
- ];
169
-
170
- function runSeamCli(args) {
171
- return new Promise((resolve) => {
172
- // 安全:只允许已知 domain,拒绝空数组或任意 domain
173
- const firstArg = Array.isArray(args) && args.length ? String(args[0]) : '';
174
- if (!ALLOWED_DOMAINS.has(firstArg)) {
175
- resolve({ ok: false, error: `Unknown domain "${firstArg}". Allowed: ${[...ALLOWED_DOMAINS].join(', ')}` });
176
- return;
177
- }
178
- execFile(
179
- process.execPath,
180
- [SEAM_CLI, ...args.map((a) => String(a))],
181
- { timeout: 20000, cwd: process.cwd(), maxBuffer: 4 * 1024 * 1024 },
182
- (err, stdout, stderr) => {
183
- if (err && !stdout) {
184
- resolve({ ok: false, error: stderr || err.message });
185
- return;
186
- }
187
- try {
188
- resolve(JSON.parse((stdout || '').trim().split('\n').pop() || '{}'));
189
- } catch (e) {
190
- resolve({ ok: false, error: `Bad CLI output: ${stdout.slice(0, 400)}` });
191
- }
192
- }
193
- );
194
- });
195
- }
196
-
197
- function sendResponse(id, result) {
198
- process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id, result }) + '\n');
199
- }
200
-
201
- function sendError(id, code, message) {
202
- process.stdout.write(
203
- JSON.stringify({ jsonrpc: '2.0', id, error: { code, message } }) + '\n'
204
- );
205
- }
206
-
207
- rl.on('line', async (line) => {
208
- let req;
209
- try {
210
- req = JSON.parse(line);
211
- } catch {
212
- return;
213
- }
214
-
215
- const { id, method, params } = req;
216
-
217
- if (method === 'initialize') {
218
- sendResponse(id, {
219
- protocolVersion: '2024-11-05',
220
- capabilities: { tools: {} },
221
- serverInfo: { name: 'seam', version: '0.5.0' },
222
- });
223
- } else if (method === 'notifications/initialized') {
224
- // no response needed
225
- } else if (method === 'tools/list') {
226
- sendResponse(id, { tools });
227
- } else if (method === 'tools/call') {
228
- const { name, arguments: args } = params;
229
- if (name !== 'seam') {
230
- sendError(id, -32601, `Unknown tool: ${name}`);
231
- return;
232
- }
233
- const argv = Array.isArray(args?.args) ? args.args : [];
234
- const result = await runSeamCli(argv);
235
- if (result.ok === false) {
236
- sendResponse(id, {
237
- content: [{ type: 'text', text: `[seam] ${result.error}` }],
238
- isError: true,
239
- });
240
- } else {
241
- const payload = result.data !== undefined ? result.data : result;
242
- const text = typeof payload === 'string' ? payload : JSON.stringify(payload, null, 2);
243
- sendResponse(id, {
244
- content: [{ type: 'text', text }],
245
- });
246
- }
247
- } else {
248
- sendError(id, -32601, `Unknown method: ${method}`);
249
- }
250
- });