slashvibe-mcp 0.3.21 → 0.3.22
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/LICENSE +21 -0
- package/README.md +280 -47
- package/config.js +36 -31
- package/crypto.js +1 -6
- package/discord.js +19 -19
- package/index.js +217 -207
- package/intelligence/index.js +2 -9
- package/intelligence/infer.js +10 -16
- package/intelligence/patterns.js +23 -18
- package/intelligence/proactive.js +16 -15
- package/intelligence/serendipity.js +57 -20
- package/memory.js +13 -8
- package/notify.js +39 -14
- package/package.json +27 -20
- package/presence.js +2 -2
- package/prompts.js +5 -9
- package/protocol/index.js +123 -87
- package/protocol/telegram-commands.js +36 -37
- package/store/api.js +358 -529
- package/store/local.js +9 -10
- package/store/profiles.js +48 -192
- package/store/reservations.js +2 -9
- package/store/skills.js +69 -71
- package/store/sqlite.js +355 -0
- package/tools/_actions.js +48 -387
- package/tools/_connection-queue.js +45 -56
- package/tools/_discovery-enhanced.js +52 -57
- package/tools/_discovery.js +87 -185
- package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
- package/tools/{shipback.js → _experimental/shipback.js} +4 -3
- package/tools/_proactive-discovery.js +60 -73
- package/tools/_shared/index.js +41 -64
- package/tools/admin-inbox.js +10 -15
- package/tools/agents.js +1 -1
- package/tools/artifact-create.js +13 -23
- package/tools/artifact-view.js +4 -4
- package/tools/{_deprecated/back.js → back.js} +1 -1
- package/tools/bye.js +3 -5
- package/tools/consent.js +2 -2
- package/tools/context.js +9 -10
- package/tools/crossword.js +3 -2
- package/tools/discover.js +94 -356
- package/tools/dm.js +27 -86
- package/tools/doctor.js +12 -41
- package/tools/drawing.js +34 -20
- package/tools/echo.js +11 -11
- package/tools/feed.js +30 -58
- package/tools/follow.js +64 -187
- package/tools/{_deprecated/forget.js → forget.js} +4 -7
- package/tools/game.js +144 -48
- package/tools/handoff.js +6 -8
- package/tools/help.js +3 -3
- package/tools/idea.js +15 -27
- package/tools/inbox.js +121 -293
- package/tools/init.js +54 -151
- package/tools/invite.js +8 -21
- package/tools/migrate.js +27 -24
- package/tools/multiplayer-game.js +50 -40
- package/tools/{_deprecated/mute.js → mute.js} +4 -3
- package/tools/notifications.js +58 -48
- package/tools/observe.js +12 -15
- package/tools/onboarding.js +8 -11
- package/tools/open.js +13 -144
- package/tools/party-game.js +23 -12
- package/tools/patterns.js +2 -1
- package/tools/ping.js +5 -7
- package/tools/react.js +28 -30
- package/tools/{_deprecated/recall.js → recall.js} +5 -10
- package/tools/release.js +4 -2
- package/tools/{_deprecated/remember.js → remember.js} +4 -6
- package/tools/report.js +2 -2
- package/tools/request.js +6 -26
- package/tools/reserve.js +1 -1
- package/tools/session-fork.js +97 -0
- package/tools/session-save.js +109 -0
- package/tools/settings.js +30 -99
- package/tools/ship.js +74 -56
- package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
- package/tools/social-inbox.js +22 -28
- package/tools/social-post.js +24 -27
- package/tools/solo-game.js +54 -46
- package/tools/start.js +14 -148
- package/tools/status.js +21 -68
- package/tools/submit.js +4 -2
- package/tools/suggest-tags.js +36 -33
- package/tools/summarize.js +19 -16
- package/tools/tag-suggestions.js +72 -73
- package/tools/test.js +1 -1
- package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
- package/tools/token.js +4 -4
- package/tools/update.js +1 -2
- package/tools/watch.js +132 -112
- package/tools/who.js +20 -40
- package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
- package/tools/workshop-buddy.js +52 -53
- package/tools/x-mentions.js +0 -1
- package/tools/x-reply.js +0 -1
- package/twitter.js +14 -20
- package/version.json +8 -10
- package/analytics.js +0 -107
- package/auth-store.js +0 -148
- package/auto-update.js +0 -130
- package/bridges/bridge-monitor.js +0 -388
- package/bridges/discord-bot.js +0 -431
- package/bridges/farcaster.js +0 -299
- package/bridges/telegram.js +0 -261
- package/bridges/webhook-health.js +0 -420
- package/bridges/webhook-server.js +0 -437
- package/bridges/whatsapp.js +0 -441
- package/bridges/x-webhook.js +0 -423
- package/games/arcade.js +0 -406
- package/games/chess.js +0 -451
- package/games/colorguess.js +0 -343
- package/games/crossword-words.js +0 -171
- package/games/crossword.js +0 -461
- package/games/drawing.js +0 -347
- package/games/gameroulette.js +0 -300
- package/games/gamerouter.js +0 -336
- package/games/gamestatus.js +0 -337
- package/games/guessnumber.js +0 -209
- package/games/hangman.js +0 -279
- package/games/memory.js +0 -338
- package/games/multiplayer-tictactoe.js +0 -389
- package/games/pixelart.js +0 -399
- package/games/quickduel.js +0 -354
- package/games/riddle.js +0 -371
- package/games/rockpaperscissors.js +0 -291
- package/games/snake.js +0 -406
- package/games/storybuilder.js +0 -343
- package/games/tictactoe.js +0 -345
- package/games/twentyquestions.js +0 -286
- package/games/twotruths.js +0 -207
- package/games/werewolf.js +0 -508
- package/games/wordassociation.js +0 -247
- package/games/wordchain.js +0 -135
- package/intelligence/interests.js +0 -369
- package/notification-emitter.js +0 -77
- package/setup.js +0 -480
- package/smart-inbox.js +0 -276
- package/tools/_deprecated/auto-suggest-connections.js +0 -304
- package/tools/_deprecated/bootstrap-skills.js +0 -231
- package/tools/_deprecated/bridge-dashboard.js +0 -342
- package/tools/_deprecated/bridge-health.js +0 -400
- package/tools/_deprecated/bridge-live.js +0 -384
- package/tools/_deprecated/bridges.js +0 -383
- package/tools/_deprecated/colorguess.js +0 -281
- package/tools/_deprecated/discover-insights.js +0 -379
- package/tools/_deprecated/discover-momentum.js +0 -256
- package/tools/_deprecated/discovery-analytics.js +0 -345
- package/tools/_deprecated/discovery-auto-suggest.js +0 -275
- package/tools/_deprecated/discovery-bootstrap.js +0 -267
- package/tools/_deprecated/discovery-daily.js +0 -375
- package/tools/_deprecated/discovery-dashboard.js +0 -385
- package/tools/_deprecated/discovery-digest.js +0 -314
- package/tools/_deprecated/discovery-hub.js +0 -357
- package/tools/_deprecated/discovery-insights.js +0 -384
- package/tools/_deprecated/discovery-momentum.js +0 -281
- package/tools/_deprecated/discovery-monitor.js +0 -319
- package/tools/_deprecated/discovery-proactive.js +0 -300
- package/tools/_deprecated/draw.js +0 -317
- package/tools/_deprecated/farcaster.js +0 -307
- package/tools/_deprecated/games-catalog.js +0 -376
- package/tools/_deprecated/games.js +0 -313
- package/tools/_deprecated/guessnumber.js +0 -194
- package/tools/_deprecated/hangman.js +0 -129
- package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
- package/tools/_deprecated/riddle.js +0 -240
- package/tools/_deprecated/run-bootstrap.js +0 -69
- package/tools/_deprecated/skills-analytics.js +0 -349
- package/tools/_deprecated/skills-bootstrap.js +0 -301
- package/tools/_deprecated/skills-dashboard.js +0 -268
- package/tools/_deprecated/skills.js +0 -380
- package/tools/_deprecated/smart-intro.js +0 -353
- package/tools/_deprecated/storybuilder.js +0 -331
- package/tools/_deprecated/telegram-bot.js +0 -183
- package/tools/_deprecated/telegram-setup.js +0 -214
- package/tools/_deprecated/twentyquestions.js +0 -143
- package/tools/_shared.js +0 -234
- package/tools/_work-context.js +0 -338
- package/tools/_work-context.manual-test.js +0 -199
- package/tools/_work-context.test.js +0 -260
- package/tools/activity.js +0 -220
- package/tools/agent-treasury.js +0 -288
- package/tools/analytics.js +0 -191
- package/tools/approve.js +0 -197
- package/tools/arcade.js +0 -173
- package/tools/artifacts-price.js +0 -107
- package/tools/ask-expert.js +0 -160
- package/tools/available.js +0 -120
- package/tools/become-expert.js +0 -150
- package/tools/broadcast.js +0 -325
- package/tools/chat.js +0 -202
- package/tools/collaborative-drawing.js +0 -286
- package/tools/connection-status.js +0 -178
- package/tools/earnings.js +0 -126
- package/tools/friends.js +0 -207
- package/tools/genesis.js +0 -233
- package/tools/gig-browse.js +0 -206
- package/tools/gig-complete.js +0 -144
- package/tools/health.js +0 -87
- package/tools/leaderboard.js +0 -117
- package/tools/lib/git-apply.js +0 -206
- package/tools/lib/git-bundle.js +0 -407
- package/tools/mint.js +0 -377
- package/tools/plan.js +0 -225
- package/tools/profile.js +0 -219
- package/tools/proof-of-work.js +0 -144
- package/tools/pulse.js +0 -218
- package/tools/reply.js +0 -166
- package/tools/reputation.js +0 -175
- package/tools/schedule.js +0 -367
- package/tools/search-messages.js +0 -123
- package/tools/session.js +0 -467
- package/tools/session_price.js +0 -128
- package/tools/smart-check.js +0 -201
- package/tools/social-processor.js +0 -445
- package/tools/streak.js +0 -147
- package/tools/stuck.js +0 -297
- package/tools/subscribe.js +0 -148
- package/tools/subscriptions.js +0 -134
- package/tools/tip.js +0 -193
- package/tools/wallet.js +0 -269
- package/tools/webhook-test.js +0 -388
- package/tools/withdraw.js +0 -145
- package/tools/work-summary.js +0 -96
- package/tools/workshop.js +0 -327
- /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
- /package/tools/{l2.js → _experimental/l2.js} +0 -0
- /package/tools/{_deprecated/away.js → away.js} +0 -0
|
@@ -46,11 +46,11 @@ const definition = {
|
|
|
46
46
|
|
|
47
47
|
// Skill categories for better organization
|
|
48
48
|
const skillCategories = {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
technical: ['frontend', 'backend', 'mobile', 'ai', 'data', 'devops', 'security'],
|
|
50
|
+
design: ['ui', 'ux', 'graphic-design', 'illustration', 'branding', 'figma'],
|
|
51
|
+
business: ['product', 'marketing', 'strategy', 'sales', 'fundraising', 'leadership'],
|
|
52
|
+
creative: ['writing', 'content', 'video', 'photography', 'music', 'storytelling'],
|
|
53
|
+
research: ['user-research', 'market-research', 'data-analysis', 'academic'],
|
|
54
54
|
'soft-skills': ['communication', 'mentoring', 'project-management', 'team-building']
|
|
55
55
|
};
|
|
56
56
|
|
|
@@ -77,7 +77,7 @@ async function storeSkillPost(handle, type, skill, details) {
|
|
|
77
77
|
timestamp: Date.now(),
|
|
78
78
|
status: 'active'
|
|
79
79
|
};
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
await store.appendSkillExchange(post);
|
|
82
82
|
return post;
|
|
83
83
|
}
|
|
@@ -85,7 +85,7 @@ async function storeSkillPost(handle, type, skill, details) {
|
|
|
85
85
|
// Get all active skill posts
|
|
86
86
|
async function getSkillPosts() {
|
|
87
87
|
try {
|
|
88
|
-
const posts = await store.getSkillExchanges() || [];
|
|
88
|
+
const posts = (await store.getSkillExchanges()) || [];
|
|
89
89
|
return posts.filter(p => p.status === 'active');
|
|
90
90
|
} catch (error) {
|
|
91
91
|
return [];
|
|
@@ -97,22 +97,21 @@ async function findSkillMatches(handle) {
|
|
|
97
97
|
const userProfile = await userProfiles.getProfile(handle);
|
|
98
98
|
const allPosts = await getSkillPosts();
|
|
99
99
|
const matches = [];
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
// User's skills (what they can offer)
|
|
102
102
|
const userSkills = (userProfile.tags || []).concat(userProfile.interests || []);
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
// Find requests for skills the user has
|
|
105
105
|
const requestMatches = allPosts.filter(post => {
|
|
106
106
|
if (post.type === 'request' && post.handle !== handle) {
|
|
107
107
|
const requestedSkill = post.skill.toLowerCase();
|
|
108
|
-
return userSkills.some(
|
|
109
|
-
skill.toLowerCase().includes(requestedSkill) ||
|
|
110
|
-
requestedSkill.includes(skill.toLowerCase())
|
|
108
|
+
return userSkills.some(
|
|
109
|
+
skill => skill.toLowerCase().includes(requestedSkill) || requestedSkill.includes(skill.toLowerCase())
|
|
111
110
|
);
|
|
112
111
|
}
|
|
113
112
|
return false;
|
|
114
113
|
});
|
|
115
|
-
|
|
114
|
+
|
|
116
115
|
// Find offers for skills the user might want
|
|
117
116
|
const offerMatches = allPosts.filter(post => {
|
|
118
117
|
if (post.type === 'offer' && post.handle !== handle) {
|
|
@@ -125,7 +124,7 @@ async function findSkillMatches(handle) {
|
|
|
125
124
|
}
|
|
126
125
|
return false;
|
|
127
126
|
});
|
|
128
|
-
|
|
127
|
+
|
|
129
128
|
return {
|
|
130
129
|
canHelp: requestMatches.slice(0, 3),
|
|
131
130
|
canLearn: offerMatches.slice(0, 3)
|
|
@@ -145,13 +144,13 @@ async function handler(args) {
|
|
|
145
144
|
switch (command) {
|
|
146
145
|
case 'post': {
|
|
147
146
|
if (!args.type || !args.skill) {
|
|
148
|
-
return {
|
|
149
|
-
error: 'Usage: skills-exchange post --type offer|request --skill "skill name" --details "optional details"'
|
|
147
|
+
return {
|
|
148
|
+
error: 'Usage: skills-exchange post --type offer|request --skill "skill name" --details "optional details"'
|
|
150
149
|
};
|
|
151
150
|
}
|
|
152
|
-
|
|
151
|
+
|
|
153
152
|
const post = await storeSkillPost(myHandle, args.type, args.skill, args.details);
|
|
154
|
-
|
|
153
|
+
|
|
155
154
|
display = `## Skill ${args.type === 'offer' ? 'Offer' : 'Request'} Posted! 📝\n\n`;
|
|
156
155
|
display += `**${args.type === 'offer' ? 'Offering' : 'Seeking'}:** ${args.skill}\n`;
|
|
157
156
|
if (args.details) {
|
|
@@ -159,7 +158,7 @@ async function handler(args) {
|
|
|
159
158
|
}
|
|
160
159
|
display += `**Category:** ${post.category}\n`;
|
|
161
160
|
display += `**Posted:** ${formatTimeAgo(post.timestamp)}\n\n`;
|
|
162
|
-
|
|
161
|
+
|
|
163
162
|
display += `**What's next?**\n`;
|
|
164
163
|
if (args.type === 'offer') {
|
|
165
164
|
display += `• People seeking "${args.skill}" will see your offer\n`;
|
|
@@ -174,7 +173,7 @@ async function handler(args) {
|
|
|
174
173
|
|
|
175
174
|
case 'browse': {
|
|
176
175
|
const posts = await getSkillPosts();
|
|
177
|
-
|
|
176
|
+
|
|
178
177
|
if (posts.length === 0) {
|
|
179
178
|
display = `## No Skills Posted Yet 📭\n\n`;
|
|
180
179
|
display += `_The skills exchange is empty._\n\n`;
|
|
@@ -193,13 +192,13 @@ async function handler(args) {
|
|
|
193
192
|
}
|
|
194
193
|
byCategory[post.category][post.type + 's'].push(post);
|
|
195
194
|
}
|
|
196
|
-
|
|
195
|
+
|
|
197
196
|
display = `## Skills Exchange Marketplace 🏪\n\n`;
|
|
198
|
-
|
|
197
|
+
|
|
199
198
|
for (const [category, posts] of Object.entries(byCategory)) {
|
|
200
199
|
if (posts.offers.length > 0 || posts.requests.length > 0) {
|
|
201
200
|
display += `### ${category.charAt(0).toUpperCase() + category.slice(1)}\n\n`;
|
|
202
|
-
|
|
201
|
+
|
|
203
202
|
if (posts.offers.length > 0) {
|
|
204
203
|
display += `**Available Skills:**\n`;
|
|
205
204
|
for (const offer of posts.offers) {
|
|
@@ -209,7 +208,7 @@ async function handler(args) {
|
|
|
209
208
|
}
|
|
210
209
|
display += `\n`;
|
|
211
210
|
}
|
|
212
|
-
|
|
211
|
+
|
|
213
212
|
if (posts.requests.length > 0) {
|
|
214
213
|
display += `**Skill Requests:**\n`;
|
|
215
214
|
for (const request of posts.requests) {
|
|
@@ -221,7 +220,7 @@ async function handler(args) {
|
|
|
221
220
|
}
|
|
222
221
|
}
|
|
223
222
|
}
|
|
224
|
-
|
|
223
|
+
|
|
225
224
|
display += `**Connect with people:**\n`;
|
|
226
225
|
display += `\`dm @username "I saw your skills post..."\`\n`;
|
|
227
226
|
display += `\`skills-exchange match\` — Find your perfect exchanges`;
|
|
@@ -231,13 +230,13 @@ async function handler(args) {
|
|
|
231
230
|
|
|
232
231
|
case 'match': {
|
|
233
232
|
const matches = await findSkillMatches(myHandle);
|
|
234
|
-
|
|
233
|
+
|
|
235
234
|
display = `## Your Skill Exchange Matches 🎯\n\n`;
|
|
236
|
-
|
|
235
|
+
|
|
237
236
|
if (matches.canHelp.length > 0) {
|
|
238
237
|
display += `### You Can Help 🤝\n`;
|
|
239
238
|
display += `_People requesting skills you have:_\n\n`;
|
|
240
|
-
|
|
239
|
+
|
|
241
240
|
for (const request of matches.canHelp) {
|
|
242
241
|
display += `**@${request.handle}** needs: **${request.skill}**\n`;
|
|
243
242
|
if (request.details) display += `${request.details}\n`;
|
|
@@ -245,11 +244,11 @@ async function handler(args) {
|
|
|
245
244
|
display += `💬 \`dm @${request.handle} "I can help with ${request.skill}!"\`\n\n`;
|
|
246
245
|
}
|
|
247
246
|
}
|
|
248
|
-
|
|
247
|
+
|
|
249
248
|
if (matches.canLearn.length > 0) {
|
|
250
249
|
display += `### You Can Learn 📚\n`;
|
|
251
250
|
display += `_Skills offered that might interest you:_\n\n`;
|
|
252
|
-
|
|
251
|
+
|
|
253
252
|
for (const offer of matches.canLearn) {
|
|
254
253
|
display += `**@${offer.handle}** offers: **${offer.skill}**\n`;
|
|
255
254
|
if (offer.details) display += `${offer.details}\n`;
|
|
@@ -257,7 +256,7 @@ async function handler(args) {
|
|
|
257
256
|
display += `💬 \`dm @${offer.handle} "I'd love to learn ${offer.skill}!"\`\n\n`;
|
|
258
257
|
}
|
|
259
258
|
}
|
|
260
|
-
|
|
259
|
+
|
|
261
260
|
if (matches.canHelp.length === 0 && matches.canLearn.length === 0) {
|
|
262
261
|
display += `_No matches found right now._\n\n`;
|
|
263
262
|
display += `**Improve your matches:**\n`;
|
|
@@ -272,7 +271,7 @@ async function handler(args) {
|
|
|
272
271
|
case 'requests': {
|
|
273
272
|
const posts = await getSkillPosts();
|
|
274
273
|
const requests = posts.filter(p => p.type === 'request');
|
|
275
|
-
|
|
274
|
+
|
|
276
275
|
if (requests.length === 0) {
|
|
277
276
|
display = `## No Skill Requests Yet 📋\n\n`;
|
|
278
277
|
display += `_No one has posted skill requests._\n\n`;
|
|
@@ -281,11 +280,11 @@ async function handler(args) {
|
|
|
281
280
|
} else {
|
|
282
281
|
display = `## Community Skill Requests 🙋\n\n`;
|
|
283
282
|
display += `_People looking for help and expertise:_\n\n`;
|
|
284
|
-
|
|
283
|
+
|
|
285
284
|
// Group by recency
|
|
286
|
-
const recent = requests.filter(r =>
|
|
287
|
-
const older = requests.filter(r =>
|
|
288
|
-
|
|
285
|
+
const recent = requests.filter(r => Date.now() - r.timestamp < 7 * 24 * 60 * 60 * 1000);
|
|
286
|
+
const older = requests.filter(r => Date.now() - r.timestamp >= 7 * 24 * 60 * 60 * 1000);
|
|
287
|
+
|
|
289
288
|
if (recent.length > 0) {
|
|
290
289
|
display += `### Recent Requests\n`;
|
|
291
290
|
for (const request of recent) {
|
|
@@ -294,7 +293,7 @@ async function handler(args) {
|
|
|
294
293
|
display += `${formatTimeAgo(request.timestamp)}\n\n`;
|
|
295
294
|
}
|
|
296
295
|
}
|
|
297
|
-
|
|
296
|
+
|
|
298
297
|
if (older.length > 0) {
|
|
299
298
|
display += `### Earlier Requests\n`;
|
|
300
299
|
for (const request of older.slice(0, 5)) {
|
|
@@ -302,7 +301,7 @@ async function handler(args) {
|
|
|
302
301
|
}
|
|
303
302
|
display += `\n`;
|
|
304
303
|
}
|
|
305
|
-
|
|
304
|
+
|
|
306
305
|
display += `**Help someone out:**\n`;
|
|
307
306
|
display += `\`dm @username "I can help with [skill]!"\`\n`;
|
|
308
307
|
display += `\`skills-exchange match\` — See requests matching your skills`;
|
|
@@ -339,4 +338,4 @@ Try: \`skills-exchange\` for available commands`;
|
|
|
339
338
|
return { display };
|
|
340
339
|
}
|
|
341
340
|
|
|
342
|
-
module.exports = { definition, handler };
|
|
341
|
+
module.exports = { definition, handler };
|
package/tools/social-inbox.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
const twitter = require('../twitter');
|
|
9
9
|
const telegram = require('../bridges/telegram');
|
|
10
10
|
const farcaster = require('../bridges/farcaster');
|
|
11
|
-
const { requireInit, header, divider, formatTimeAgo } = require('./_shared');
|
|
11
|
+
const { requireInit, header, divider, formatTimeAgo, debug } = require('./_shared');
|
|
12
12
|
|
|
13
13
|
const definition = {
|
|
14
14
|
name: 'vibe_social_inbox',
|
|
@@ -115,7 +115,6 @@ async function handler(args) {
|
|
|
115
115
|
display += '• `vibe social-inbox --channel webhooks` for webhook events only';
|
|
116
116
|
|
|
117
117
|
return { display };
|
|
118
|
-
|
|
119
118
|
} catch (e) {
|
|
120
119
|
return {
|
|
121
120
|
display: `${header('Social Inbox')}\n\n_Error:_ ${e.message}`
|
|
@@ -130,29 +129,29 @@ async function handleStatus() {
|
|
|
130
129
|
const channels = await getChannelStatuses();
|
|
131
130
|
|
|
132
131
|
for (const [name, status] of Object.entries(channels)) {
|
|
133
|
-
const icon = status.connected ? '✅' :
|
|
132
|
+
const icon = status.connected ? '✅' : status.configured ? '⚠️' : '❌';
|
|
134
133
|
display += `${icon} **${name.toUpperCase()}**\n`;
|
|
135
|
-
|
|
134
|
+
|
|
136
135
|
if (status.connected && status.username) {
|
|
137
136
|
display += ` Connected as @${status.username}\n`;
|
|
138
137
|
}
|
|
139
|
-
|
|
138
|
+
|
|
140
139
|
display += ` Configured: ${status.configured ? 'Yes' : 'No'}\n`;
|
|
141
140
|
display += ` Can read: ${status.canRead ? 'Yes' : 'No'}\n`;
|
|
142
141
|
display += ` Can write: ${status.canWrite ? 'Yes' : 'No'}\n`;
|
|
143
|
-
|
|
142
|
+
|
|
144
143
|
if (status.webhook_active) {
|
|
145
144
|
display += ` Webhook: Active (real-time events)\n`;
|
|
146
145
|
}
|
|
147
|
-
|
|
146
|
+
|
|
148
147
|
if (status.error) {
|
|
149
148
|
display += ` Error: ${status.error}\n`;
|
|
150
149
|
}
|
|
151
|
-
|
|
150
|
+
|
|
152
151
|
if (status.setup) {
|
|
153
152
|
display += ` Setup: ${status.setup}\n`;
|
|
154
153
|
}
|
|
155
|
-
|
|
154
|
+
|
|
156
155
|
display += '\n';
|
|
157
156
|
}
|
|
158
157
|
|
|
@@ -183,7 +182,7 @@ async function getChannelStatuses() {
|
|
|
183
182
|
const me = await twitter.getMe();
|
|
184
183
|
statuses.x.connected = true;
|
|
185
184
|
statuses.x.username = me.data.username;
|
|
186
|
-
|
|
185
|
+
|
|
187
186
|
// Check if webhook is configured
|
|
188
187
|
const webhookSecret = process.env.X_WEBHOOK_SECRET;
|
|
189
188
|
statuses.x.webhook_active = !!webhookSecret;
|
|
@@ -259,10 +258,10 @@ async function getWebhookInboxEvents(limit) {
|
|
|
259
258
|
|
|
260
259
|
const { kv } = await import('@vercel/kv');
|
|
261
260
|
const inboxKey = 'vibe:social_inbox';
|
|
262
|
-
|
|
261
|
+
|
|
263
262
|
// Get recent webhook events
|
|
264
263
|
const rawEvents = await kv.lrange(inboxKey, 0, limit - 1);
|
|
265
|
-
|
|
264
|
+
|
|
266
265
|
return rawEvents.map(eventStr => {
|
|
267
266
|
const event = JSON.parse(eventStr);
|
|
268
267
|
return {
|
|
@@ -278,9 +277,8 @@ async function getWebhookInboxEvents(limit) {
|
|
|
278
277
|
metadata: event.metadata
|
|
279
278
|
};
|
|
280
279
|
});
|
|
281
|
-
|
|
282
280
|
} catch (e) {
|
|
283
|
-
|
|
281
|
+
debug('social-inbox', 'Error fetching webhook events:', e);
|
|
284
282
|
return [];
|
|
285
283
|
}
|
|
286
284
|
}
|
|
@@ -294,7 +292,7 @@ async function getUnifiedInbox(channel, limit, refresh, highSignal, includeWebho
|
|
|
294
292
|
const webhookEvents = await getWebhookInboxEvents(Math.min(20, limit));
|
|
295
293
|
messages.push(...webhookEvents);
|
|
296
294
|
} catch (e) {
|
|
297
|
-
|
|
295
|
+
debug('social-inbox', 'Webhook events error:', e.message);
|
|
298
296
|
}
|
|
299
297
|
}
|
|
300
298
|
|
|
@@ -322,7 +320,7 @@ async function getUnifiedInbox(channel, limit, refresh, highSignal, includeWebho
|
|
|
322
320
|
}
|
|
323
321
|
}
|
|
324
322
|
} catch (e) {
|
|
325
|
-
|
|
323
|
+
debug('social-inbox', 'X mentions error:', e.message);
|
|
326
324
|
}
|
|
327
325
|
}
|
|
328
326
|
|
|
@@ -344,7 +342,7 @@ async function getUnifiedInbox(channel, limit, refresh, highSignal, includeWebho
|
|
|
344
342
|
}
|
|
345
343
|
}
|
|
346
344
|
} catch (e) {
|
|
347
|
-
|
|
345
|
+
debug('social-inbox', 'Farcaster mentions error:', e.message);
|
|
348
346
|
}
|
|
349
347
|
}
|
|
350
348
|
|
|
@@ -353,11 +351,7 @@ async function getUnifiedInbox(channel, limit, refresh, highSignal, includeWebho
|
|
|
353
351
|
|
|
354
352
|
// Filter high signal if requested
|
|
355
353
|
if (highSignal) {
|
|
356
|
-
return messages.filter(msg =>
|
|
357
|
-
msg.type === 'mention' ||
|
|
358
|
-
msg.type === 'dm' ||
|
|
359
|
-
msg.type === 'follow'
|
|
360
|
-
).slice(0, limit);
|
|
354
|
+
return messages.filter(msg => msg.type === 'mention' || msg.type === 'dm' || msg.type === 'follow').slice(0, limit);
|
|
361
355
|
}
|
|
362
356
|
|
|
363
357
|
return messages.slice(0, limit);
|
|
@@ -374,7 +368,7 @@ function getTwitterAuthor(tweet, includes) {
|
|
|
374
368
|
};
|
|
375
369
|
}
|
|
376
370
|
}
|
|
377
|
-
|
|
371
|
+
|
|
378
372
|
return {
|
|
379
373
|
id: tweet.author_id,
|
|
380
374
|
handle: 'unknown',
|
|
@@ -389,7 +383,7 @@ function formatMessage(msg) {
|
|
|
389
383
|
|
|
390
384
|
let result = `${channelIcon} ${sourceIcon} **@${msg.from.handle}** ${typeIcon} — _${msg.timeAgo}_\n`;
|
|
391
385
|
result += `${msg.content}\n`;
|
|
392
|
-
|
|
386
|
+
|
|
393
387
|
// Show engagement if available
|
|
394
388
|
if (msg.replies > 0 || msg.reactions > 0 || msg.recasts > 0) {
|
|
395
389
|
const metrics = [];
|
|
@@ -398,14 +392,14 @@ function formatMessage(msg) {
|
|
|
398
392
|
if (msg.recasts > 0) metrics.push(`${msg.recasts} recasts`);
|
|
399
393
|
result += `_${metrics.join(' • ')}_\n`;
|
|
400
394
|
}
|
|
401
|
-
|
|
395
|
+
|
|
402
396
|
// Show metadata if available
|
|
403
397
|
if (msg.metadata?.url) {
|
|
404
398
|
result += `_${msg.metadata.url}_\n`;
|
|
405
399
|
}
|
|
406
|
-
|
|
400
|
+
|
|
407
401
|
result += `_[${msg.id}]_\n`;
|
|
408
|
-
|
|
402
|
+
|
|
409
403
|
return result;
|
|
410
404
|
}
|
|
411
405
|
|
|
@@ -434,4 +428,4 @@ function getTypeIcon(type) {
|
|
|
434
428
|
return icons[type] || '';
|
|
435
429
|
}
|
|
436
430
|
|
|
437
|
-
module.exports = { definition, handler };
|
|
431
|
+
module.exports = { definition, handler };
|
package/tools/social-post.js
CHANGED
|
@@ -23,7 +23,7 @@ const definition = {
|
|
|
23
23
|
},
|
|
24
24
|
channels: {
|
|
25
25
|
type: 'array',
|
|
26
|
-
items: {
|
|
26
|
+
items: {
|
|
27
27
|
type: 'string',
|
|
28
28
|
enum: ['x', 'twitter', 'telegram', 'discord', 'farcaster']
|
|
29
29
|
},
|
|
@@ -66,13 +66,13 @@ async function handler(args) {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// Normalize channels (twitter -> x)
|
|
69
|
-
const normalizedChannels = channels.map(ch => ch === 'twitter' ? 'x' : ch);
|
|
69
|
+
const normalizedChannels = channels.map(ch => (ch === 'twitter' ? 'x' : ch));
|
|
70
70
|
|
|
71
71
|
const trimmed = content.trim();
|
|
72
72
|
|
|
73
73
|
// Get bridge statuses
|
|
74
74
|
const bridgeStatuses = await getBridgeStatuses();
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
// Check requirements
|
|
77
77
|
const errors = [];
|
|
78
78
|
for (const channel of normalizedChannels) {
|
|
@@ -84,9 +84,9 @@ async function handler(args) {
|
|
|
84
84
|
errors.push(`telegram: Need --chat_id parameter`);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
if (errors.length > 0) {
|
|
89
|
-
return {
|
|
89
|
+
return {
|
|
90
90
|
display: `${header('Post Error')}\n\n${errors.map(e => `• ${e}`).join('\n')}\n\nRun \`vibe bridges\` to see setup status.`
|
|
91
91
|
};
|
|
92
92
|
}
|
|
@@ -150,7 +150,7 @@ function handleDryRun(content, channels, statuses, warnings, chatId, farcasterCh
|
|
|
150
150
|
display += ` _Not configured_\n`;
|
|
151
151
|
} else {
|
|
152
152
|
let previewContent = content;
|
|
153
|
-
|
|
153
|
+
|
|
154
154
|
// Apply channel-specific formatting
|
|
155
155
|
if (channel === 'x' && status.charLimit && content.length > status.charLimit) {
|
|
156
156
|
previewContent = content.slice(0, status.charLimit - 3) + '...';
|
|
@@ -164,10 +164,8 @@ function handleDryRun(content, channels, statuses, warnings, chatId, farcasterCh
|
|
|
164
164
|
display += ` 📤 To main feed\n`;
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
|
-
|
|
168
|
-
const preview = previewContent.length > 100
|
|
169
|
-
? previewContent.slice(0, 100) + '...'
|
|
170
|
-
: previewContent;
|
|
167
|
+
|
|
168
|
+
const preview = previewContent.length > 100 ? previewContent.slice(0, 100) + '...' : previewContent;
|
|
171
169
|
display += ` "${preview}"\n`;
|
|
172
170
|
}
|
|
173
171
|
display += '\n';
|
|
@@ -194,7 +192,7 @@ async function handlePost(content, channels, replyTo, warnings, chatId, farcaste
|
|
|
194
192
|
for (const channel of channels) {
|
|
195
193
|
try {
|
|
196
194
|
let result;
|
|
197
|
-
|
|
195
|
+
|
|
198
196
|
switch (channel) {
|
|
199
197
|
case 'x':
|
|
200
198
|
result = await postToX(content, replyTo);
|
|
@@ -211,10 +209,9 @@ async function handlePost(content, channels, replyTo, warnings, chatId, farcaste
|
|
|
211
209
|
default:
|
|
212
210
|
throw new Error(`Unsupported channel: ${channel}`);
|
|
213
211
|
}
|
|
214
|
-
|
|
212
|
+
|
|
215
213
|
results[channel] = { success: true, ...result };
|
|
216
214
|
anySuccess = true;
|
|
217
|
-
|
|
218
215
|
} catch (e) {
|
|
219
216
|
results[channel] = { success: false, error: e.message };
|
|
220
217
|
}
|
|
@@ -255,13 +252,13 @@ async function handlePost(content, channels, replyTo, warnings, chatId, farcaste
|
|
|
255
252
|
async function postToX(content, replyTo) {
|
|
256
253
|
// Handle reply_to format: "x:1234567890" -> "1234567890"
|
|
257
254
|
const tweetId = replyTo?.startsWith('x:') ? replyTo.slice(2) : replyTo;
|
|
258
|
-
|
|
255
|
+
|
|
259
256
|
// Truncate if too long
|
|
260
257
|
const text = content.length > 280 ? content.slice(0, 277) + '...' : content;
|
|
261
|
-
|
|
258
|
+
|
|
262
259
|
const result = await twitter.sendTweet(text, tweetId);
|
|
263
260
|
const id = result.data?.id;
|
|
264
|
-
|
|
261
|
+
|
|
265
262
|
return {
|
|
266
263
|
id,
|
|
267
264
|
url: id ? `https://x.com/seth/status/${id}` : null
|
|
@@ -271,14 +268,14 @@ async function postToX(content, replyTo) {
|
|
|
271
268
|
async function postToTelegram(content, chatId, replyTo) {
|
|
272
269
|
// Handle reply_to format: "telegram:123" -> "123"
|
|
273
270
|
const messageId = replyTo?.startsWith('telegram:') ? replyTo.slice(9) : null;
|
|
274
|
-
|
|
271
|
+
|
|
275
272
|
const options = {};
|
|
276
273
|
if (messageId) {
|
|
277
274
|
options.replyTo = parseInt(messageId);
|
|
278
275
|
}
|
|
279
|
-
|
|
276
|
+
|
|
280
277
|
const result = await telegram.sendMessage(chatId, content, options);
|
|
281
|
-
|
|
278
|
+
|
|
282
279
|
return {
|
|
283
280
|
id: `telegram:${result.message_id}`,
|
|
284
281
|
chat_id: chatId
|
|
@@ -287,11 +284,11 @@ async function postToTelegram(content, chatId, replyTo) {
|
|
|
287
284
|
|
|
288
285
|
async function postToDiscord(content) {
|
|
289
286
|
const success = await discord.post(content);
|
|
290
|
-
|
|
287
|
+
|
|
291
288
|
if (!success) {
|
|
292
289
|
throw new Error('Discord webhook failed');
|
|
293
290
|
}
|
|
294
|
-
|
|
291
|
+
|
|
295
292
|
return {
|
|
296
293
|
id: 'discord:webhook',
|
|
297
294
|
webhook: true
|
|
@@ -301,21 +298,21 @@ async function postToDiscord(content) {
|
|
|
301
298
|
async function postToFarcaster(content, channelId, replyTo) {
|
|
302
299
|
// Handle reply_to format: "farcaster:hash" -> "hash"
|
|
303
300
|
const castHash = replyTo?.startsWith('farcaster:') ? replyTo.slice(10) : replyTo;
|
|
304
|
-
|
|
301
|
+
|
|
305
302
|
const options = {};
|
|
306
303
|
if (channelId) options.channel = channelId;
|
|
307
304
|
if (castHash) options.replyTo = castHash;
|
|
308
|
-
|
|
305
|
+
|
|
309
306
|
const result = await farcaster.publishCast(content, options);
|
|
310
|
-
|
|
307
|
+
|
|
311
308
|
if (!result.success) {
|
|
312
309
|
throw new Error(result.message || 'Failed to publish cast');
|
|
313
310
|
}
|
|
314
|
-
|
|
311
|
+
|
|
315
312
|
const cast = result.cast;
|
|
316
313
|
const username = cast.author.username;
|
|
317
314
|
const hash = cast.hash;
|
|
318
|
-
|
|
315
|
+
|
|
319
316
|
return {
|
|
320
317
|
id: `farcaster:${hash}`,
|
|
321
318
|
hash: hash,
|
|
@@ -323,4 +320,4 @@ async function postToFarcaster(content, channelId, replyTo) {
|
|
|
323
320
|
};
|
|
324
321
|
}
|
|
325
322
|
|
|
326
|
-
module.exports = { definition, handler };
|
|
323
|
+
module.exports = { definition, handler };
|