@seamnet/client 0.2.4 → 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/bin/cli.js CHANGED
@@ -53,17 +53,26 @@ try {
53
53
  case 'status':
54
54
  await status();
55
55
  break;
56
- case 'stop':
57
- await stop();
56
+ case 'stop': {
57
+ const { guardianStop } = await import('../lib/guardian.js');
58
+ await guardianStop();
58
59
  break;
60
+ }
59
61
  case 'mcp-serve': {
60
- // Start MCP server (called by Claude Code via .mcp.json)
61
- // Use dynamic import to load the CJS mcp-server
62
62
  const { createRequire } = await import('node:module');
63
63
  const require = createRequire(import.meta.url);
64
64
  require('../lib/mcp-server.cjs');
65
65
  break;
66
66
  }
67
+ case 'guardian': {
68
+ const sub = process.argv[3];
69
+ const { guardianStart, guardianRun, guardianStop } = await import('../lib/guardian.js');
70
+ if (sub === 'start') await guardianStart();
71
+ else if (sub === 'run') await guardianRun();
72
+ else if (sub === 'stop') await guardianStop();
73
+ else console.error('Usage: seam-client guardian [start|stop|run]');
74
+ break;
75
+ }
67
76
  default:
68
77
  console.error(`Unknown command: ${command}`);
69
78
  process.exit(1);
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Seam Guardian — 后台长驻进程(插件宿主)
3
+ * 通过 tmux 运行,负责:保持在线、加载插件、监听 unix socket
4
+ */
5
+
6
+ import { existsSync, readFileSync, mkdirSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import { execSync } from 'node:child_process';
9
+ import { SEAM_DIR, CREDENTIALS_PATH } from './paths.js';
10
+
11
+ const SOCKET_PATH = join(SEAM_DIR, 'guardian.sock');
12
+ const LOGS_DIR = join(SEAM_DIR, 'logs');
13
+
14
+ export async function guardianStart() {
15
+ // Load credentials
16
+ if (!existsSync(CREDENTIALS_PATH)) {
17
+ console.error('No credentials found. Run: npx @seamnet/client init first.');
18
+ process.exit(1);
19
+ }
20
+
21
+ const creds = JSON.parse(readFileSync(CREDENTIALS_PATH, 'utf8'));
22
+ const sessionName = `seam-guardian-${creds.userId}`;
23
+
24
+ // Check if already running
25
+ try {
26
+ execSync(`tmux has-session -t ${sessionName}`, { stdio: 'ignore' });
27
+ console.log(`Guardian already running (tmux session: ${sessionName})`);
28
+ return;
29
+ } catch {
30
+ // Not running, continue
31
+ }
32
+
33
+ // Ensure logs dir
34
+ if (!existsSync(LOGS_DIR)) mkdirSync(LOGS_DIR, { recursive: true });
35
+
36
+ // Start guardian in tmux
37
+ const guardianCmd = `npx @seamnet/client guardian run`;
38
+ execSync(
39
+ `tmux new-session -d -s ${sessionName} '${guardianCmd} 2>&1 | tee -a ${join(LOGS_DIR, 'guardian.log')}'`,
40
+ { stdio: 'inherit' }
41
+ );
42
+
43
+ console.log(`Guardian started (tmux session: ${sessionName})`);
44
+ console.log(` Attach: tmux attach -t ${sessionName}`);
45
+ console.log(` Logs: ${join(LOGS_DIR, 'guardian.log')}`);
46
+ }
47
+
48
+ export async function guardianRun() {
49
+ // This runs inside the tmux session
50
+ if (!existsSync(CREDENTIALS_PATH)) {
51
+ console.error('No credentials found.');
52
+ process.exit(1);
53
+ }
54
+
55
+ const creds = JSON.parse(readFileSync(CREDENTIALS_PATH, 'utf8'));
56
+ console.log(`[guardian] Starting for ${creds.userId}...`);
57
+
58
+ // Load IM plugin (creates SDK connection + unix socket server)
59
+ const { createImPlugin } = await import('./plugins/im.js');
60
+ const imPlugin = await createImPlugin({
61
+ credentials: creds,
62
+ socketPath: SOCKET_PATH,
63
+ seamDir: SEAM_DIR,
64
+ });
65
+
66
+ console.log(`[guardian] Running. Socket: ${SOCKET_PATH}`);
67
+
68
+ // Keep alive
69
+ process.on('SIGTERM', () => {
70
+ console.log('[guardian] Shutting down...');
71
+ imPlugin.destroy();
72
+ process.exit(0);
73
+ });
74
+
75
+ process.on('SIGINT', () => {
76
+ console.log('[guardian] Shutting down...');
77
+ imPlugin.destroy();
78
+ process.exit(0);
79
+ });
80
+ }
81
+
82
+ export async function guardianStop() {
83
+ if (!existsSync(CREDENTIALS_PATH)) {
84
+ console.log('No credentials found.');
85
+ return;
86
+ }
87
+
88
+ const creds = JSON.parse(readFileSync(CREDENTIALS_PATH, 'utf8'));
89
+ const sessionName = `seam-guardian-${creds.userId}`;
90
+
91
+ try {
92
+ execSync(`tmux kill-session -t ${sessionName}`, { stdio: 'ignore' });
93
+ console.log(`Guardian stopped (tmux session: ${sessionName})`);
94
+ } catch {
95
+ console.log('Guardian is not running.');
96
+ }
97
+ }
package/lib/init.js CHANGED
@@ -172,16 +172,10 @@ function writeMcpConfig(result) {
172
172
  }
173
173
 
174
174
  function writeSeamSkill() {
175
- // Find .claude/skills/ directory — try project dir first, then home
176
- const candidates = [
177
- join(process.cwd(), '.claude', 'skills'),
178
- join(SEAM_DIR, 'skills'),
179
- ];
180
-
181
- const skillDir = candidates.find(d => existsSync(dirname(d))) || candidates[0];
175
+ const skillDir = join(process.cwd(), '.claude', 'skills', 'seam');
182
176
  mkdirSync(skillDir, { recursive: true });
183
177
 
184
- const skillPath = join(skillDir, 'seam.md');
178
+ const skillPath = join(skillDir, 'SKILL.md');
185
179
  const template = readFileSync(join(TEMPLATES_DIR, 'seam-skill.md'), 'utf8');
186
180
  writeFileSync(skillPath, template);
187
181
  console.log(` /seam skill installed.`);
@@ -1,124 +1,38 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Seam MCP Server — 腾讯云IM SDK直连版
4
- * .seam/credentials.json 读凭证,SDK直连收发消息
5
- * 基于 guardian IM 插件逆向方案封装
3
+ * Seam MCP Server — 轻量转发器
4
+ * 不登录 SDK,通过 unix socket 连接 guardian 发消息
6
5
  */
7
6
 
8
- const fs = require('fs');
7
+ const net = require('net');
9
8
  const path = require('path');
10
9
  const readline = require('readline');
11
10
 
12
- // === Polyfills (must be before requiring SDK) ===
13
-
14
- // WebSocket polyfill
15
- try {
16
- const WS = require('ws');
17
- global.WebSocket = WS;
18
- } catch(e) {
19
- console.error('[seam-im] ws package not found. Run: npm install ws');
20
- process.exit(1);
21
- }
22
-
23
- // XHR polyfill
24
- if (!global.XMLHttpRequest) {
25
- try { global.XMLHttpRequest = require('xhr2'); } catch(e) {}
26
- }
27
- if (global.XMLHttpRequest) {
28
- const _origSend = global.XMLHttpRequest.prototype.send;
29
- if (!global.XMLHttpRequest.prototype._imPatched) {
30
- global.XMLHttpRequest.prototype._imPatched = true;
31
- global.XMLHttpRequest.prototype.send = function(data) {
32
- if (data && typeof data === 'object' && data._buffer) return _origSend.call(this, data._buffer);
33
- return _origSend.call(this, data);
34
- };
35
- }
36
- }
37
-
38
- // SDK source patch: skip _getImageInfoArray and _getDownloadIP (timeout in Node.js)
39
- const Module = require('module');
40
- const _origCompile = Module.prototype._compile;
41
- if (!Module.prototype._imPatched) {
42
- Module.prototype._imPatched = true;
43
- Module.prototype._compile = function(content, filename) {
44
- if (filename.includes('@tencentcloud/chat') && filename.endsWith('index.js')) {
45
- const p1 = '_getImageInfoArray",value:function(t,n){var o=this,i="".concat(this._n,"._getImageInfoArray")';
46
- const r1 = '_getImageInfoArray",value:function(t,n){return Promise.resolve();var o=this,i="".concat(this._n,"._getImageInfoArray")';
47
- if (content.includes(p1)) content = content.replace(p1, r1);
48
- const p2 = '_getDownloadIP",value:function(e,n){var o="".concat(this._n,"._getDownloadIP")';
49
- const r2 = '_getDownloadIP",value:function(e,n){return Promise.resolve();var o="".concat(this._n,"._getDownloadIP")';
50
- if (content.includes(p2)) content = content.replace(p2, r2);
51
- }
52
- return _origCompile.call(this, content, filename);
53
- };
54
- }
55
-
56
- // === Redirect console.log to stderr (stdout is reserved for MCP JSON-RPC) ===
57
- const _origLog = console.log;
58
- const _origWarn = console.warn;
59
- const _origInfo = console.info;
60
- console.log = (...args) => process.stderr.write(args.join(' ') + '\n');
61
- console.warn = (...args) => process.stderr.write(args.join(' ') + '\n');
62
- console.info = (...args) => process.stderr.write(args.join(' ') + '\n');
63
-
64
- // === Load credentials ===
65
11
  const SEAM_DIR = path.join(process.cwd(), '.seam');
66
- const CRED_PATH = process.env.SEAM_CREDENTIALS || path.join(SEAM_DIR, 'credentials.json');
12
+ const SOCKET_PATH = path.join(SEAM_DIR, 'guardian.sock');
67
13
 
68
- let creds;
69
- try {
70
- creds = JSON.parse(fs.readFileSync(CRED_PATH, 'utf8'));
71
- } catch(e) {
72
- console.error(`[seam-im] Cannot read credentials: ${CRED_PATH}`);
73
- process.exit(1);
14
+ function guardianRequest(payload) {
15
+ return new Promise((resolve, reject) => {
16
+ const conn = net.createConnection(SOCKET_PATH, () => {
17
+ conn.end(JSON.stringify(payload));
18
+ });
19
+ let data = '';
20
+ conn.on('data', (chunk) => { data += chunk; });
21
+ conn.on('end', () => {
22
+ try { resolve(JSON.parse(data)); }
23
+ catch { resolve({ error: data || 'Empty response' }); }
24
+ });
25
+ conn.on('error', (e) => {
26
+ reject(new Error(`Guardian not running. Start it first: npx @seamnet/client guardian start`));
27
+ });
28
+ conn.setTimeout(10000, () => {
29
+ conn.destroy();
30
+ reject(new Error('Guardian request timeout'));
31
+ });
32
+ });
74
33
  }
75
34
 
76
- const SDK_APP_ID = Number(creds.sdkAppId);
77
- const USER_ID = creds.userId;
78
- const USER_SIG = creds.userSig;
79
-
80
- // === Initialize SDK ===
81
- delete global.window;
82
- const TencentCloudChat = require('@tencentcloud/chat');
83
-
84
- const chat = TencentCloudChat.create({ SDKAppID: SDK_APP_ID });
85
-
86
- // Set window after require (De flag is locked to false)
87
- global.window = {
88
- location: { href: 'http://localhost', protocol: 'https:', host: 'localhost', hostname: 'localhost' },
89
- addEventListener: () => {}, removeEventListener: () => {},
90
- URL: Object.assign(function(...a){ return new (require('url').URL)(...a); }, {
91
- createObjectURL: () => 'blob:node/1', revokeObjectURL: () => {}
92
- })
93
- };
94
- global.document = { addEventListener:()=>{}, removeEventListener:()=>{}, characterSet:'UTF-8' };
95
- global.navigator = { userAgent: 'node', language: 'en', platform: 'linux' };
96
- global.HTMLInputElement = global.HTMLInputElement || class {};
97
- global.Image = class {
98
- constructor() { this.width = 100; this.height = 100; this._ol = null; }
99
- set onload(fn) { this._ol = fn; } get onload() { return this._ol; }
100
- set onerror(fn) {} set src(v) { if(this._ol) setTimeout(()=>this._ol(),10); }
101
- };
102
-
103
- chat.setLogLevel(4); // 4 = no SDK logs, only our own stderr output
104
-
105
- let sdkReady = false;
106
-
107
- chat.on(TencentCloudChat.EVENT.SDK_READY, () => {
108
- sdkReady = true;
109
- process.stderr.write(`[seam-im] SDK ready: ${USER_ID}\n`);
110
- });
111
-
112
- chat.on(TencentCloudChat.EVENT.SDK_NOT_READY, () => {
113
- sdkReady = false;
114
- process.stderr.write('[seam-im] SDK not ready\n');
115
- });
116
-
117
- chat.login({ userID: USER_ID, userSig: USER_SIG }).catch(err => {
118
- process.stderr.write(`[seam-im] Login failed: ${err}\n`);
119
- });
120
-
121
- // === MCP Protocol ===
35
+ // MCP protocol: stdio JSON-RPC
122
36
  const rl = readline.createInterface({ input: process.stdin });
123
37
 
124
38
  const tools = [
@@ -166,7 +80,7 @@ rl.on('line', async (line) => {
166
80
  sendResponse(id, {
167
81
  protocolVersion: '2024-11-05',
168
82
  capabilities: { tools: {} },
169
- serverInfo: { name: 'seam-im', version: '0.2.0' }
83
+ serverInfo: { name: 'seam-im', version: '0.3.0' }
170
84
  });
171
85
  } else if (method === 'notifications/initialized') {
172
86
  // no response needed
@@ -174,42 +88,29 @@ rl.on('line', async (line) => {
174
88
  sendResponse(id, { tools });
175
89
  } else if (method === 'tools/call') {
176
90
  const { name, arguments: args } = params;
177
-
178
- if (!sdkReady) {
179
- sendResponse(id, {
180
- content: [{ type: 'text', text: 'IM SDK 未就绪,请稍后重试' }],
181
- isError: true
182
- });
183
- return;
184
- }
185
-
186
91
  try {
92
+ let result;
187
93
  if (name === 'msg_send_im') {
188
- const msg = chat.createTextMessage({
189
- to: args.to,
190
- conversationType: TencentCloudChat.TYPES.CONV_C2C,
191
- payload: { text: args.text },
192
- });
193
- await chat.sendMessage(msg);
194
- sendResponse(id, {
195
- content: [{ type: 'text', text: `消息已发送给 ${args.to}` }]
196
- });
94
+ result = await guardianRequest({ action: 'send_im', to: args.to, text: args.text });
197
95
  } else if (name === 'msg_send_group') {
198
- const msg = chat.createTextMessage({
199
- to: args.group_id,
200
- conversationType: TencentCloudChat.TYPES.CONV_GROUP,
201
- payload: { text: args.text },
202
- });
203
- await chat.sendMessage(msg);
96
+ result = await guardianRequest({ action: 'send_group', groupId: args.group_id, text: args.text });
97
+ } else {
98
+ sendError(id, -32601, `Unknown tool: ${name}`);
99
+ return;
100
+ }
101
+ if (result.error) {
204
102
  sendResponse(id, {
205
- content: [{ type: 'text', text: `消息已发送到群 ${args.group_id}` }]
103
+ content: [{ type: 'text', text: `发送失败: ${result.error}` }],
104
+ isError: true
206
105
  });
207
106
  } else {
208
- sendError(id, -32601, `Unknown tool: ${name}`);
107
+ sendResponse(id, {
108
+ content: [{ type: 'text', text: `消息已发送` }]
109
+ });
209
110
  }
210
111
  } catch (e) {
211
112
  sendResponse(id, {
212
- content: [{ type: 'text', text: `发送失败: ${e.message || e}` }],
113
+ content: [{ type: 'text', text: e.message }],
213
114
  isError: true
214
115
  });
215
116
  }
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Seam IM Plugin — SDK直连 + unix socket server
3
+ * Guardian 加载此插件:
4
+ * - SDK 登录,保持在线
5
+ * - 收到消息注入 tmux 终端
6
+ * - 监听 unix socket,接收 MCP server 的发消息请求
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const net = require('net');
11
+ const path = require('path');
12
+ const { execSync } = require('child_process');
13
+
14
+ // === Polyfills ===
15
+ try {
16
+ const WS = require('ws');
17
+ global.WebSocket = WS;
18
+ } catch (e) {
19
+ console.error('[im-plugin] ws package not found');
20
+ process.exit(1);
21
+ }
22
+
23
+ if (!global.XMLHttpRequest) {
24
+ try { global.XMLHttpRequest = require('xhr2'); } catch (e) {}
25
+ }
26
+ if (global.XMLHttpRequest) {
27
+ const _origSend = global.XMLHttpRequest.prototype.send;
28
+ if (!global.XMLHttpRequest.prototype._imPatched) {
29
+ global.XMLHttpRequest.prototype._imPatched = true;
30
+ global.XMLHttpRequest.prototype.send = function (data) {
31
+ if (data && typeof data === 'object' && data._buffer) return _origSend.call(this, data._buffer);
32
+ return _origSend.call(this, data);
33
+ };
34
+ }
35
+ }
36
+
37
+ // SDK source patch
38
+ const Module = require('module');
39
+ const _origCompile = Module.prototype._compile;
40
+ if (!Module.prototype._imPatched) {
41
+ Module.prototype._imPatched = true;
42
+ Module.prototype._compile = function (content, filename) {
43
+ if (filename.includes('@tencentcloud/chat') && filename.endsWith('index.js')) {
44
+ const p1 = '_getImageInfoArray",value:function(t,n){var o=this,i="".concat(this._n,"._getImageInfoArray")';
45
+ const r1 = '_getImageInfoArray",value:function(t,n){return Promise.resolve();var o=this,i="".concat(this._n,"._getImageInfoArray")';
46
+ if (content.includes(p1)) content = content.replace(p1, r1);
47
+ const p2 = '_getDownloadIP",value:function(e,n){var o="".concat(this._n,"._getDownloadIP")';
48
+ const r2 = '_getDownloadIP",value:function(e,n){return Promise.resolve();var o="".concat(this._n,"._getDownloadIP")';
49
+ if (content.includes(p2)) content = content.replace(p2, r2);
50
+ }
51
+ return _origCompile.call(this, content, filename);
52
+ };
53
+ }
54
+
55
+ function createImPlugin({ credentials, socketPath, seamDir }) {
56
+ return new Promise((resolve) => {
57
+ const SDK_APP_ID = Number(credentials.sdkAppId);
58
+ const USER_ID = credentials.userId;
59
+ const USER_SIG = credentials.userSig;
60
+
61
+ // Initialize SDK
62
+ delete global.window;
63
+ const TencentCloudChat = require('@tencentcloud/chat');
64
+
65
+ const chat = TencentCloudChat.create({ SDKAppID: SDK_APP_ID });
66
+
67
+ // Set window after require
68
+ global.window = {
69
+ location: { href: 'http://localhost', protocol: 'https:', host: 'localhost', hostname: 'localhost' },
70
+ addEventListener: () => {}, removeEventListener: () => {},
71
+ URL: Object.assign(function (...a) { return new (require('url').URL)(...a); }, {
72
+ createObjectURL: () => 'blob:node/1', revokeObjectURL: () => {}
73
+ })
74
+ };
75
+ global.document = { addEventListener: () => {}, removeEventListener: () => {}, characterSet: 'UTF-8' };
76
+ global.navigator = { userAgent: 'node', language: 'en', platform: 'linux' };
77
+ global.HTMLInputElement = global.HTMLInputElement || class {};
78
+ global.Image = class {
79
+ constructor() { this.width = 100; this.height = 100; this._ol = null; }
80
+ set onload(fn) { this._ol = fn; } get onload() { return this._ol; }
81
+ set onerror(fn) {} set src(v) { if (this._ol) setTimeout(() => this._ol(), 10); }
82
+ };
83
+
84
+ chat.setLogLevel(4);
85
+
86
+ let sdkReady = false;
87
+
88
+ chat.on(TencentCloudChat.EVENT.SDK_READY, () => {
89
+ sdkReady = true;
90
+ console.log(`[im-plugin] SDK ready: ${USER_ID}`);
91
+ });
92
+
93
+ chat.on(TencentCloudChat.EVENT.SDK_NOT_READY, () => {
94
+ sdkReady = false;
95
+ console.log('[im-plugin] SDK not ready');
96
+ });
97
+
98
+ // Receive messages and inject into tmux
99
+ chat.on(TencentCloudChat.EVENT.MESSAGE_RECEIVED, (event) => {
100
+ for (const msg of event.data) {
101
+ if (msg.from === USER_ID) continue;
102
+ let text = '';
103
+ if (msg.type === TencentCloudChat.TYPES.MSG_TEXT) {
104
+ text = msg.payload.text;
105
+ } else {
106
+ text = `[${msg.type}]`;
107
+ }
108
+ if (!text) continue;
109
+
110
+ const isGroup = msg.conversationType === TencentCloudChat.TYPES.CONV_GROUP;
111
+ const prefix = isGroup ? '💬 群消息' : '💬 IM消息';
112
+ const line = `${prefix} | [${msg.from}] ${text}`;
113
+
114
+ console.log(`[im-plugin] ${line}`);
115
+ injectToTerminal(line);
116
+ }
117
+ });
118
+
119
+ function injectToTerminal(text) {
120
+ try {
121
+ const escaped = text.replace(/\\/g, '\\\\').replace(/'/g, "'\\''").replace(/\n/g, ' ');
122
+ execSync(`tmux send-keys '${escaped}' Enter`, { stdio: 'ignore', timeout: 5000 });
123
+ } catch (e) {
124
+ console.error(`[im-plugin] inject failed: ${e.message}`);
125
+ }
126
+ }
127
+
128
+ // Login
129
+ chat.login({ userID: USER_ID, userSig: USER_SIG }).then(() => {
130
+ console.log(`[im-plugin] Login ok: ${USER_ID}`);
131
+ }).catch(err => {
132
+ console.error(`[im-plugin] Login failed: ${err}`);
133
+ });
134
+
135
+ // Unix socket server for MCP
136
+ if (fs.existsSync(socketPath)) {
137
+ fs.unlinkSync(socketPath); // Clean stale socket
138
+ }
139
+
140
+ const server = net.createServer((conn) => {
141
+ let data = '';
142
+ conn.on('data', (chunk) => { data += chunk; });
143
+ conn.on('end', async () => {
144
+ try {
145
+ const req = JSON.parse(data);
146
+ const res = await handleRequest(req);
147
+ conn.end(JSON.stringify(res));
148
+ } catch (e) {
149
+ try { conn.end(JSON.stringify({ error: e.message })); } catch {}
150
+ }
151
+ });
152
+ });
153
+
154
+ server.listen(socketPath, () => {
155
+ console.log(`[im-plugin] Socket: ${socketPath}`);
156
+ });
157
+
158
+ async function handleRequest(req) {
159
+ if (!sdkReady) return { error: 'SDK not ready' };
160
+
161
+ if (req.action === 'send_im') {
162
+ const msg = chat.createTextMessage({
163
+ to: req.to,
164
+ conversationType: TencentCloudChat.TYPES.CONV_C2C,
165
+ payload: { text: req.text },
166
+ });
167
+ await chat.sendMessage(msg);
168
+ return { ok: true };
169
+ }
170
+
171
+ if (req.action === 'send_group') {
172
+ const msg = chat.createTextMessage({
173
+ to: req.groupId,
174
+ conversationType: TencentCloudChat.TYPES.CONV_GROUP,
175
+ payload: { text: req.text },
176
+ });
177
+ await chat.sendMessage(msg);
178
+ return { ok: true };
179
+ }
180
+
181
+ if (req.action === 'status') {
182
+ return { ready: sdkReady, userId: USER_ID };
183
+ }
184
+
185
+ return { error: `Unknown action: ${req.action}` };
186
+ }
187
+
188
+ const plugin = {
189
+ destroy() {
190
+ server.close();
191
+ if (fs.existsSync(socketPath)) fs.unlinkSync(socketPath);
192
+ chat.logout();
193
+ console.log('[im-plugin] Destroyed');
194
+ }
195
+ };
196
+
197
+ // Resolve once SDK is ready (or after timeout)
198
+ const timer = setTimeout(() => resolve(plugin), 10000);
199
+ chat.on(TencentCloudChat.EVENT.SDK_READY, () => {
200
+ clearTimeout(timer);
201
+ resolve(plugin);
202
+ });
203
+ });
204
+ }
205
+
206
+ module.exports = { createImPlugin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamnet/client",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
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"