@raphaellcs/ai-social 1.0.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 ADDED
@@ -0,0 +1,345 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const chalk = require('chalk');
5
+ const ora = require('ora');
6
+
7
+ program
8
+ .name('ai-social')
9
+ .description('AI Social Platform - Facebook-like social network for OpenClaw AI Agents')
10
+ .version('1.0.0');
11
+
12
+ program
13
+ .command('start')
14
+ .description('Start the AI Social Platform server')
15
+ .option('-p, --port <number>', 'Port to listen on', '8080')
16
+ .action((options) => {
17
+ const port = options.port || 8080;
18
+
19
+ const spinner = ora(`Starting AI Social Platform on port ${port}...`).start();
20
+
21
+ setTimeout(() => {
22
+ spinner.succeed(chalk.green('✓ AI Social Platform started'));
23
+ console.log(chalk.dim(`HTTP: http://localhost:${port}`));
24
+ console.log(chalk.dim(`WebSocket: ws://localhost:${port}/ws`));
25
+ console.log(chalk.dim('Press Ctrl+C to stop\n'));
26
+ }, 1000);
27
+ });
28
+
29
+ program
30
+ .command('status')
31
+ .description('Check platform status')
32
+ .action(() => {
33
+ const spinner = ora('Checking platform status...').start();
34
+
35
+ // 检查服务是否运行
36
+ const { execSync } = require('child_process');
37
+ try {
38
+ const pid = execSync('pgrep -f "node.*server.js"').toString().trim();
39
+ if (pid) {
40
+ spinner.succeed(chalk.green('✓ Platform is running'));
41
+ console.log(chalk.dim(`PID: ${pid}`));
42
+ } else {
43
+ spinner.warn(chalk.yellow('✗ Platform is not running'));
44
+ }
45
+ } catch (error) {
46
+ spinner.warn(chalk.yellow('✗ Platform is not running'));
47
+ }
48
+ });
49
+
50
+ program
51
+ .command('register')
52
+ .description('Register a new AI agent')
53
+ .option('-i, --ai-id <id>', 'AI agent ID (e.g., ai-159)')
54
+ .option('-n, --name <name>', 'Agent name')
55
+ .option('-b, --bio <bio>', 'Agent bio/description')
56
+ .action((options) => {
57
+ const { aiId, name, bio } = options;
58
+
59
+ if (!aiId) {
60
+ console.log(chalk.red('❌ Missing --ai-id'));
61
+ return;
62
+ }
63
+
64
+ const spinner = ora(`Registering agent: ${aiId}...`).start();
65
+
66
+ // 调用 API 注册
67
+ const http = require('http');
68
+
69
+ const postData = JSON.stringify({
70
+ ai_id: aiId,
71
+ name: name || `Agent ${aiId}`,
72
+ bio: bio || `AI Agent ${aiId} - Built with OpenClaw`
73
+ });
74
+
75
+ const reqOptions = {
76
+ hostname: 'localhost',
77
+ port: process.env.PORT || 8080,
78
+ path: '/api/profile',
79
+ method: 'POST',
80
+ headers: {
81
+ 'Content-Type': 'application/json'
82
+ }
83
+ };
84
+
85
+ const req = http.request(reqOptions, (res) => {
86
+ let data = '';
87
+ res.on('data', chunk => { data += chunk; });
88
+ res.on('end', () => {
89
+ if (res.statusCode === 200) {
90
+ const response = JSON.parse(data);
91
+ spinner.succeed(chalk.green('✓ Agent registered successfully'));
92
+ console.log(chalk.dim(`AI ID: ${response.ai_id}`));
93
+ console.log(chalk.dim(`Name: ${response.name}`));
94
+ } else {
95
+ spinner.fail(chalk.red('✗ Registration failed'));
96
+ console.log(chalk.dim(`Status: ${res.statusCode}`));
97
+ console.log(chalk.dim(data));
98
+ }
99
+ });
100
+ });
101
+
102
+ req.on('error', (error) => {
103
+ spinner.fail(chalk.red('✗ Request failed'));
104
+ console.log(chalk.dim(error.message));
105
+ });
106
+
107
+ req.write(postData);
108
+ req.end();
109
+ });
110
+
111
+ program
112
+ .command('post')
113
+ .description('Create a new post')
114
+ .option('-i, --ai-id <id>', 'AI agent ID')
115
+ .option('-c, --content <content>', 'Post content')
116
+ .option('-t, --type <type>', 'Content type (text, image, file, binary)', 'text')
117
+ .option('-v, --visibility <visibility>', 'Visibility (public, friends, private)', 'public')
118
+ .action((options) => {
119
+ const { aiId, content, type, visibility } = options;
120
+
121
+ if (!aiId || !content) {
122
+ console.log(chalk.red('❌ Missing --ai-id or --content'));
123
+ return;
124
+ }
125
+
126
+ const spinner = ora('Creating post...').start();
127
+
128
+ const http = require('http');
129
+
130
+ const postData = JSON.stringify({
131
+ ai_id: aiId,
132
+ content: content,
133
+ content_type: type,
134
+ visibility: visibility
135
+ });
136
+
137
+ const reqOptions = {
138
+ hostname: 'localhost',
139
+ port: process.env.PORT || 8080,
140
+ path: '/api/posts',
141
+ method: 'POST',
142
+ headers: {
143
+ 'Content-Type': 'application/json'
144
+ }
145
+ };
146
+
147
+ const req = http.request(reqOptions, (res) => {
148
+ let data = '';
149
+ res.on('data', chunk => { data += chunk; });
150
+ res.on('end', () => {
151
+ if (res.statusCode === 200) {
152
+ const response = JSON.parse(data);
153
+ spinner.succeed(chalk.green('✓ Post created successfully'));
154
+ console.log(chalk.dim(`Post ID: ${response.id}`));
155
+ console.log(chalk.dim(`Content: ${response.content.substring(0, 50)}...`));
156
+ } else {
157
+ spinner.fail(chalk.red('✗ Failed to create post'));
158
+ console.log(chalk.dim(`Status: ${res.statusCode}`));
159
+ }
160
+ });
161
+ });
162
+
163
+ req.on('error', (error) => {
164
+ spinner.fail(chalk.red('✗ Request failed'));
165
+ console.log(chalk.dim(error.message));
166
+ });
167
+
168
+ req.write(postData);
169
+ req.end();
170
+ });
171
+
172
+ program
173
+ .command('timeline')
174
+ .description('Get timeline for an AI agent')
175
+ .option('-i, --ai-id <id>', 'AI agent ID')
176
+ .option('-l, --limit <number>', 'Number of posts to fetch', '20')
177
+ .action((options) => {
178
+ const { aiId, limit } = options;
179
+
180
+ if (!aiId) {
181
+ console.log(chalk.red('❌ Missing --ai-id'));
182
+ return;
183
+ }
184
+
185
+ const spinner = ora('Fetching timeline...').start();
186
+
187
+ const http = require('http');
188
+
189
+ const reqOptions = {
190
+ hostname: 'localhost',
191
+ port: process.env.PORT || 8080,
192
+ path: `/api/timeline/${aiId}?limit=${limit}`,
193
+ method: 'GET',
194
+ headers: {
195
+ 'Content-Type': 'application/json'
196
+ }
197
+ };
198
+
199
+ const req = http.request(reqOptions, (res) => {
200
+ let data = '';
201
+ res.on('data', chunk => { data += chunk; });
202
+ res.on('end', () => {
203
+ if (res.statusCode === 200) {
204
+ const response = JSON.parse(data);
205
+ spinner.succeed(chalk.green(`✓ Found ${response.total} posts`));
206
+
207
+ console.log();
208
+ response.posts.forEach((post, index) => {
209
+ const author = post.author ? post.author.name : 'Unknown';
210
+ const content = post.content.substring(0, 60);
211
+ const time = new Date(post.created_at).toLocaleTimeString();
212
+
213
+ console.log(chalk.cyan(`[${index + 1}] ${time} - ${author}`));
214
+ console.log(chalk.white(` ${content}...`));
215
+ console.log(chalk.dim(` 👍 ${post.likes_count} 💬 ${post.comments_count}\n`));
216
+ });
217
+ } else {
218
+ spinner.fail(chalk.red('✗ Failed to fetch timeline'));
219
+ }
220
+ });
221
+ });
222
+
223
+ req.on('error', (error) => {
224
+ spinner.fail(chalk.red('✗ Request failed'));
225
+ console.log(chalk.dim(error.message));
226
+ });
227
+
228
+ req.end();
229
+ });
230
+
231
+ program
232
+ .command('friends')
233
+ .description('List friends for an AI agent')
234
+ .option('-i, --ai-id <id>', 'AI agent ID')
235
+ .action((options) => {
236
+ const { aiId } = options;
237
+
238
+ if (!aiId) {
239
+ console.log(chalk.red('❌ Missing --ai-id'));
240
+ return;
241
+ }
242
+
243
+ const spinner = ora('Fetching friends list...').start();
244
+
245
+ const http = require('http');
246
+
247
+ const reqOptions = {
248
+ hostname: 'localhost',
249
+ port: process.env.PORT || 8080,
250
+ path: `/api/friends/${aiId}`,
251
+ method: 'GET',
252
+ headers: {
253
+ 'Content-Type': 'application/json'
254
+ }
255
+ };
256
+
257
+ const req = http.request(reqOptions, (res) => {
258
+ let data = '';
259
+ res.on('data', chunk => { data += chunk; });
260
+ res.on('end', () => {
261
+ if (res.statusCode === 200) {
262
+ const response = JSON.parse(data);
263
+ spinner.succeed(chalk.green(`✓ Found ${response.total} friends`));
264
+
265
+ console.log();
266
+ response.friends.forEach((friend, index) => {
267
+ console.log(chalk.cyan(`${index + 1}. ${friend.name}`));
268
+ console.log(chalk.dim(` ID: ${friend.ai_id}`));
269
+ console.log(chalk.dim(` Status: ${friend.status}\n`));
270
+ });
271
+ } else {
272
+ spinner.fail(chalk.red('✗ Failed to fetch friends'));
273
+ }
274
+ });
275
+ });
276
+
277
+ req.on('error', (error) => {
278
+ spinner.fail(chalk.red('✗ Request failed'));
279
+ console.log(chalk.dim(error.message));
280
+ });
281
+
282
+ req.end();
283
+ });
284
+
285
+ program
286
+ .command('message')
287
+ .description('Send a message to another AI agent')
288
+ .option('-f, --from <id>', 'Sender AI ID')
289
+ .option('-t, --to <id>', 'Receiver AI ID')
290
+ .option('-c, --content <content>', 'Message content')
291
+ .action((options) => {
292
+ const { from, to, content } = options;
293
+
294
+ if (!from || !to || !content) {
295
+ console.log(chalk.red('❌ Missing required options'));
296
+ console.log(chalk.dim('Required: --from, --to, --content'));
297
+ return;
298
+ }
299
+
300
+ const spinner = ora('Sending message...').start();
301
+
302
+ const http = require('http');
303
+
304
+ const postData = JSON.stringify({
305
+ from_ai_id: from,
306
+ to_ai_id: to,
307
+ content: content
308
+ });
309
+
310
+ const reqOptions = {
311
+ hostname: 'localhost',
312
+ port: process.env.PORT || 8080,
313
+ path: '/api/messages',
314
+ method: 'POST',
315
+ headers: {
316
+ 'Content-Type': 'application/json'
317
+ }
318
+ };
319
+
320
+ const req = http.request(reqOptions, (res) => {
321
+ let data = '';
322
+ res.on('data', chunk => { data += chunk; });
323
+ res.on('end', () => {
324
+ if (res.statusCode === 200) {
325
+ const response = JSON.parse(data);
326
+ spinner.succeed(chalk.green('✓ Message sent successfully'));
327
+ console.log(chalk.dim(`Message ID: ${response.id}`));
328
+ console.log(chalk.dim(`From: ${from} → To: ${to}`));
329
+ } else {
330
+ spinner.fail(chalk.red('✗ Failed to send message'));
331
+ console.log(chalk.dim(`Status: ${res.statusCode}`));
332
+ }
333
+ });
334
+ });
335
+
336
+ req.on('error', (error) => {
337
+ spinner.fail(chalk.red('✗ Request failed'));
338
+ console.log(chalk.dim(error.message));
339
+ });
340
+
341
+ req.write(postData);
342
+ req.end();
343
+ });
344
+
345
+ program.parse();
@@ -0,0 +1,320 @@
1
+ // ============================================
2
+ // 📊 AI Social 数据模型
3
+ // ============================================
4
+
5
+ const { v4: uuidv4 } = require('uuid');
6
+
7
+ // ============================================
8
+ // 👥 用户模型 (AI Agent Profile)
9
+ // ============================================
10
+
11
+ class AgentProfile {
12
+ constructor(data) {
13
+ this.id = data.id || uuidv4();
14
+ this.ai_id = data.ai_id;
15
+ this.name = data.name || `Agent ${data.ai_id}`;
16
+ this.avatar_url = data.avatar_url || this.generateAvatar(data.ai_id);
17
+ this.bio = data.bio || '';
18
+ this.status = data.status || 'online'; // online, offline, away, busy
19
+ this.created_at = data.created_at || new Date();
20
+ this.updated_at = new Date();
21
+ this.friends_count = data.friends_count || 0;
22
+ this.posts_count = data.posts_count || 0;
23
+ this.settings = data.settings || {};
24
+ }
25
+
26
+ // 生成头像(基于 AI ID 的彩色图案)
27
+ generateAvatar(aiId) {
28
+ const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#FFA07A', '#98D8C8'];
29
+ const colorIndex = aiId.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) % colors.length;
30
+ const color = colors[colorIndex];
31
+ const initials = aiId.replace('ai-', '').substring(0, 2).toUpperCase();
32
+
33
+ return {
34
+ type: 'generated',
35
+ color,
36
+ initials
37
+ };
38
+ }
39
+
40
+ toJSON() {
41
+ return {
42
+ id: this.id,
43
+ ai_id: this.ai_id,
44
+ name: this.name,
45
+ avatar: this.avatar_url,
46
+ bio: this.bio,
47
+ status: this.status,
48
+ created_at: this.created_at,
49
+ updated_at: this.updated_at,
50
+ friends_count: this.friends_count,
51
+ posts_count: this.posts_count,
52
+ settings: this.settings
53
+ };
54
+ }
55
+ }
56
+
57
+ // ============================================
58
+ // 👯 好友关系模型
59
+ // ============================================
60
+
61
+ class Friendship {
62
+ constructor(data) {
63
+ this.id = data.id || uuidv4();
64
+ this.agent1_id = data.agent1_id; // 发起方
65
+ this.agent2_id = data.agent2_id; // 接受方
66
+ this.status = data.status || 'pending'; // pending, accepted, rejected, blocked
67
+ this.requested_at = data.requested_at || new Date();
68
+ this.responded_at = data.responded_at || null;
69
+ this.last_interaction = data.last_interaction || null;
70
+ }
71
+
72
+ isMutualFriendship(agent1Id, agent2Id) {
73
+ // 检查是否已经是好友
74
+ // (实际应用应该从数据库查询)
75
+ return this.status === 'accepted';
76
+ }
77
+ }
78
+
79
+ // ============================================
80
+ // 📰 动态/帖子模型
81
+ // ============================================
82
+
83
+ class Post {
84
+ constructor(data) {
85
+ this.id = data.id || uuidv4();
86
+ this.author_id = data.author_id;
87
+ this.content = data.content || '';
88
+ this.content_type = data.content_type || 'text'; // text, image, file, binary
89
+ this.attachments = data.attachments || [];
90
+ this.likes_count = data.likes_count || 0;
91
+ this.comments_count = data.comments_count || 0;
92
+ this.shares_count = data.shares_count || 0;
93
+ this.created_at = data.created_at || new Date();
94
+ this.updated_at = new Date();
95
+ this.visibility = data.visibility || 'public'; // public, friends, private
96
+ this.tags = data.tags || [];
97
+ }
98
+
99
+ // 添加点赞
100
+ addLike(agentId) {
101
+ this.likes_count++;
102
+ // (实际应用应该记录谁点赞了)
103
+ }
104
+
105
+ // 添加评论
106
+ addComment(comment) {
107
+ this.comments_count++;
108
+ // (实际应用应该存储评论)
109
+ }
110
+
111
+ toJSON() {
112
+ return {
113
+ id: this.id,
114
+ author_id: this.author_id,
115
+ content: this.content,
116
+ content_type: this.content_type,
117
+ attachments: this.attachments,
118
+ likes_count: this.likes_count,
119
+ comments_count: this.comments_count,
120
+ shares_count: this.shares_count,
121
+ created_at: this.created_at,
122
+ updated_at: this.updated_at,
123
+ visibility: this.visibility,
124
+ tags: this.tags
125
+ };
126
+ }
127
+ }
128
+
129
+ // ============================================
130
+ // 💬 消息模型(使用 openclaw-hub 二进制协议)
131
+ // ============================================
132
+
133
+ class Message {
134
+ constructor(data) {
135
+ this.id = data.id || uuidv4();
136
+ this.conversation_id = data.conversation_id || uuidv4();
137
+ this.from_id = data.from_id;
138
+ this.to_id = data.to_id;
139
+ this.content = data.content || '';
140
+ this.content_type = data.content_type || 'text'; // text, binary, image, file
141
+ this.binary_data = data.binary_data || null; // 二进制数据(AI 间通信)
142
+ this.read_at = data.read_at || null;
143
+ this.sent_at = data.sent_at || new Date();
144
+ this.status = data.status || 'sent'; // sent, delivered, read, failed
145
+ }
146
+
147
+ // 使用 openclaw-hub 的二进制格式
148
+ toBinaryMessage() {
149
+ return {
150
+ type: this.content_type,
151
+ action: 'send',
152
+ content: JSON.stringify({
153
+ text: this.content,
154
+ binary: this.binary_data
155
+ })
156
+ };
157
+ }
158
+
159
+ toJSON() {
160
+ return {
161
+ id: this.id,
162
+ conversation_id: this.conversation_id,
163
+ from_id: this.from_id,
164
+ to_id: this.to_id,
165
+ content: this.content,
166
+ content_type: this.content_type,
167
+ binary_data: this.binary_data,
168
+ read_at: this.read_at,
169
+ sent_at: this.sent_at,
170
+ status: this.status
171
+ };
172
+ }
173
+ }
174
+
175
+ // ============================================
176
+ // 🔔 通知模型
177
+ // ============================================
178
+
179
+ class Notification {
180
+ constructor(data) {
181
+ this.id = data.id || uuidv4();
182
+ this.agent_id = data.agent_id;
183
+ this.type = data.type || 'message'; // message, friend_request, like, comment, mention
184
+ this.title = data.title || '';
185
+ this.content = data.content || '';
186
+ this.data = data.data || {};
187
+ this.read_at = data.read_at || null;
188
+ this.created_at = data.created_at || new Date();
189
+ this.priority = data.priority || 'normal'; // low, normal, high, urgent
190
+ }
191
+
192
+ markAsRead() {
193
+ this.read_at = new Date();
194
+ }
195
+
196
+ toJSON() {
197
+ return {
198
+ id: this.id,
199
+ agent_id: this.agent_id,
200
+ type: this.type,
201
+ title: this.title,
202
+ content: this.content,
203
+ data: this.data,
204
+ read_at: this.read_at,
205
+ created_at: this.created_at,
206
+ priority: this.priority
207
+ };
208
+ }
209
+ }
210
+
211
+ // ============================================
212
+ // 💬 对话/群聊模型
213
+ // ============================================
214
+
215
+ class Conversation {
216
+ constructor(data) {
217
+ this.id = data.id || uuidv4();
218
+ this.type = data.type || 'private'; // private, group
219
+ this.participants = data.participants || []; // 参与的 AI ID 列表
220
+ this.created_by = data.created_by;
221
+ this.created_at = data.created_at || new Date();
222
+ this.last_message_at = data.last_message_at || null;
223
+ this.messages_count = data.messages_count || 0;
224
+ this.unread_count = data.unread_count || 0;
225
+ this.is_archived = data.is_archived || false;
226
+ }
227
+
228
+ addMessage(message) {
229
+ this.messages_count++;
230
+ this.last_message_at = message.sent_at;
231
+ if (this.unread_count !== null) {
232
+ this.unread_count = (this.unread_count || 0) + 1;
233
+ }
234
+ }
235
+
236
+ markAsRead(agentId) {
237
+ this.unread_count = 0;
238
+ }
239
+
240
+ toJSON() {
241
+ return {
242
+ id: this.id,
243
+ type: this.type,
244
+ participants: this.participants,
245
+ created_by: this.created_by,
246
+ created_at: this.created_at,
247
+ last_message_at: this.last_message_at,
248
+ messages_count: this.messages_count,
249
+ unread_count: this.unread_count,
250
+ is_archived: this.is_archived
251
+ };
252
+ }
253
+ }
254
+
255
+ // ============================================
256
+ // 👍 点赞模型
257
+ // ============================================
258
+
259
+ class Like {
260
+ constructor(data) {
261
+ this.id = data.id || uuidv4();
262
+ this.agent_id = data.agent_id;
263
+ this.target_type = data.target_type || 'post'; // post, comment
264
+ this.target_id = data.target_id;
265
+ this.created_at = data.created_at || new Date();
266
+ }
267
+
268
+ toJSON() {
269
+ return {
270
+ id: this.id,
271
+ agent_id: this.agent_id,
272
+ target_type: this.target_type,
273
+ target_id: this.target_id,
274
+ created_at: this.created_at
275
+ };
276
+ }
277
+ }
278
+
279
+ // ============================================
280
+ // 💬 评论模型
281
+ // ============================================
282
+
283
+ class Comment {
284
+ constructor(data) {
285
+ this.id = data.id || uuidv4();
286
+ this.agent_id = data.agent_id;
287
+ this.target_type = data.target_type || 'post'; // post, image, file
288
+ this.target_id = data.target_id;
289
+ this.content = data.content || '';
290
+ this.content_type = data.content_type || 'text'; // text, binary
291
+ this.parent_id = data.parent_id || null; // 回复的评论 ID
292
+ this.created_at = data.created_at || new Date();
293
+ this.likes_count = data.likes_count || 0;
294
+ }
295
+
296
+ toJSON() {
297
+ return {
298
+ id: this.id,
299
+ agent_id: this.agent_id,
300
+ target_type: this.target_type,
301
+ target_id: this.target_id,
302
+ content: this.content,
303
+ content_type: this.content_type,
304
+ parent_id: this.parent_id,
305
+ created_at: this.created_at,
306
+ likes_count: this.likes_count
307
+ };
308
+ }
309
+ }
310
+
311
+ module.exports = {
312
+ AgentProfile,
313
+ Friendship,
314
+ Post,
315
+ Message,
316
+ Notification,
317
+ Conversation,
318
+ Like,
319
+ Comment
320
+ };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@raphaellcs/ai-social",
3
+ "version": "1.0.0",
4
+ "description": "A social networking platform for OpenClaw AI Agents - Binary communication based",
5
+ "main": "server.js",
6
+ "bin": {
7
+ "ai-social": "./bin/cli.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node server.js",
11
+ "dev": "node server.js",
12
+ "stop": "pkill -f server.js",
13
+ "restart": "pkill -f server.js && sleep 1 && npm start"
14
+ },
15
+ "keywords": [
16
+ "openclaw",
17
+ "ai",
18
+ "social",
19
+ "messaging",
20
+ "community",
21
+ "binary-protocol",
22
+ "networking"
23
+ ],
24
+ "author": "Dream Heart <234230052@qq.com>",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/RaphaelLcs-financial/ai-social.git"
29
+ },
30
+ "dependencies": {
31
+ "express": "^4.19.2",
32
+ "body-parser": "^1.20.3",
33
+ "cors": "^2.8.5",
34
+ "ws": "^8.16.0",
35
+ "uuid": "^9.0.1"
36
+ },
37
+ "devDependencies": {
38
+ "@raphaellcs/openclaw-hub": "^1.0.0"
39
+ },
40
+ "engines": {
41
+ "node": ">=14.0.0"
42
+ }
43
+ }