slashvibe-mcp 0.2.9 → 0.3.13
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/README.md +1 -0
- package/analytics.js +107 -0
- package/auth-store.js +148 -0
- package/auto-update.js +130 -0
- package/bridges/bridge-monitor.js +388 -0
- package/bridges/discord-bot.js +431 -0
- package/bridges/farcaster.js +299 -0
- package/bridges/telegram.js +261 -0
- package/bridges/webhook-health.js +420 -0
- package/bridges/webhook-server.js +437 -0
- package/bridges/whatsapp.js +441 -0
- package/bridges/x-webhook.js +423 -0
- package/config.js +154 -5
- package/crypto.js +3 -8
- package/games/arcade.js +406 -0
- package/games/chess.js +451 -0
- package/games/colorguess.js +343 -0
- package/games/crossword-words.js +171 -0
- package/games/crossword.js +461 -0
- package/games/drawing.js +347 -0
- package/games/gameroulette.js +300 -0
- package/games/gamerouter.js +336 -0
- package/games/gamestatus.js +337 -0
- package/games/guessnumber.js +209 -0
- package/games/hangman.js +279 -0
- package/games/memory.js +338 -0
- package/games/multiplayer-tictactoe.js +389 -0
- package/games/pixelart.js +399 -0
- package/games/quickduel.js +354 -0
- package/games/riddle.js +371 -0
- package/games/rockpaperscissors.js +291 -0
- package/games/snake.js +406 -0
- package/games/storybuilder.js +343 -0
- package/games/tictactoe.js +345 -0
- package/games/twentyquestions.js +286 -0
- package/games/twotruths.js +207 -0
- package/games/werewolf.js +508 -0
- package/games/wordassociation.js +247 -0
- package/games/wordchain.js +135 -0
- package/index.js +105 -216
- package/intelligence/index.js +45 -0
- package/intelligence/infer.js +316 -0
- package/intelligence/interests.js +369 -0
- package/intelligence/patterns.js +651 -0
- package/intelligence/proactive.js +358 -0
- package/intelligence/serendipity.js +306 -0
- package/memory.js +166 -0
- package/notification-emitter.js +77 -0
- package/notify.js +102 -1
- package/package.json +21 -7
- package/prompts.js +1 -1
- package/protocol/index.js +161 -1
- package/setup.js +402 -0
- package/store/api.js +528 -82
- package/store/profiles.js +160 -12
- package/tools/_actions.js +463 -16
- package/tools/_deprecated/auto-suggest-connections.js +304 -0
- package/tools/_deprecated/bootstrap-skills.js +231 -0
- package/tools/_deprecated/bridge-dashboard.js +342 -0
- package/tools/_deprecated/bridge-health.js +400 -0
- package/tools/_deprecated/bridge-live.js +384 -0
- package/tools/_deprecated/bridges.js +383 -0
- package/tools/_deprecated/colorguess.js +281 -0
- package/tools/_deprecated/discover-insights.js +379 -0
- package/tools/_deprecated/discover-momentum.js +256 -0
- package/tools/_deprecated/discovery-analytics.js +345 -0
- package/tools/_deprecated/discovery-auto-suggest.js +275 -0
- package/tools/_deprecated/discovery-bootstrap.js +267 -0
- package/tools/_deprecated/discovery-daily.js +375 -0
- package/tools/_deprecated/discovery-dashboard.js +385 -0
- package/tools/_deprecated/discovery-digest.js +314 -0
- package/tools/_deprecated/discovery-hub.js +357 -0
- package/tools/_deprecated/discovery-insights.js +384 -0
- package/tools/_deprecated/discovery-momentum.js +281 -0
- package/tools/_deprecated/discovery-monitor.js +319 -0
- package/tools/_deprecated/discovery-proactive.js +300 -0
- package/tools/_deprecated/draw.js +317 -0
- package/tools/_deprecated/farcaster.js +307 -0
- package/tools/_deprecated/forget.js +119 -0
- package/tools/_deprecated/games-catalog.js +376 -0
- package/tools/_deprecated/games.js +313 -0
- package/tools/_deprecated/guessnumber.js +194 -0
- package/tools/_deprecated/hangman.js +129 -0
- package/tools/_deprecated/multiplayer-tictactoe.js +303 -0
- package/tools/_deprecated/recall.js +147 -0
- package/tools/_deprecated/remember.js +86 -0
- package/tools/_deprecated/riddle.js +240 -0
- package/tools/_deprecated/run-bootstrap.js +69 -0
- package/tools/_deprecated/skills-analytics.js +349 -0
- package/tools/_deprecated/skills-bootstrap.js +301 -0
- package/tools/_deprecated/skills-dashboard.js +268 -0
- package/tools/_deprecated/skills.js +380 -0
- package/tools/_deprecated/smart-intro.js +353 -0
- package/tools/_deprecated/storybuilder.js +331 -0
- package/tools/_deprecated/telegram-bot.js +183 -0
- package/tools/_deprecated/telegram-setup.js +214 -0
- package/tools/_deprecated/twentyquestions.js +143 -0
- package/tools/_discovery-enhanced.js +290 -0
- package/tools/_discovery.js +439 -0
- package/tools/_proactive-discovery.js +301 -0
- package/tools/_shared/index.js +64 -0
- package/tools/_shared.js +234 -0
- package/tools/_work-context.js +338 -0
- package/tools/_work-context.manual-test.js +199 -0
- package/tools/_work-context.test.js +260 -0
- package/tools/activity.js +220 -0
- package/tools/admin-inbox.js +218 -0
- package/tools/agent-treasury.js +288 -0
- package/tools/analytics.js +191 -0
- package/tools/approve.js +197 -0
- package/tools/arcade.js +173 -0
- package/tools/artifact-create.js +17 -11
- package/tools/artifact-view.js +31 -1
- package/tools/artifacts-price.js +47 -43
- package/tools/ask-expert.js +160 -0
- package/tools/available.js +120 -0
- package/tools/become-expert.js +150 -0
- package/tools/broadcast.js +286 -0
- package/tools/chat.js +202 -0
- package/tools/collaborative-drawing.js +286 -0
- package/tools/connection-status.js +178 -0
- package/tools/crossword.js +17 -1
- package/tools/discover.js +350 -34
- package/tools/dm.js +115 -73
- package/tools/drawing.js +1 -1
- package/tools/earnings.js +126 -0
- package/tools/echo.js +16 -0
- package/tools/feed.js +51 -7
- package/tools/follow.js +224 -0
- package/tools/friends.js +207 -0
- package/tools/game.js +2 -2
- package/tools/genesis.js +233 -0
- package/tools/gig-browse.js +206 -0
- package/tools/gig-complete.js +144 -0
- package/tools/handoff.js +7 -1
- package/tools/help.js +4 -4
- package/tools/idea.js +9 -2
- package/tools/inbox.js +334 -28
- package/tools/init.js +727 -36
- package/tools/invite.js +15 -4
- package/tools/l2-bridge.js +272 -0
- package/tools/l2-status.js +217 -0
- package/tools/l2.js +206 -0
- package/tools/migrate.js +156 -0
- package/tools/mint.js +377 -0
- package/tools/multiplayer-game.js +1 -1
- package/tools/notifications.js +415 -0
- package/tools/observe.js +200 -0
- package/tools/onboarding.js +147 -0
- package/tools/open.js +143 -12
- package/tools/party-game.js +2 -2
- package/tools/plan.js +225 -0
- package/tools/presence-agent.js +7 -0
- package/tools/proof-of-work.js +100 -104
- package/tools/pulse.js +218 -0
- package/tools/reply.js +166 -0
- package/tools/report.js +2 -2
- package/tools/reputation.js +175 -0
- package/tools/request.js +17 -3
- package/tools/schedule.js +367 -0
- package/tools/search-messages.js +123 -0
- package/tools/session.js +420 -0
- package/tools/session_price.js +128 -0
- package/tools/settings.js +126 -3
- package/tools/ship.js +31 -8
- package/tools/shipback.js +326 -0
- package/tools/smart-check.js +201 -0
- package/tools/social-processor.js +445 -0
- package/tools/solo-game.js +1 -1
- package/tools/start.js +335 -93
- package/tools/status.js +53 -6
- package/tools/stuck.js +297 -0
- package/tools/subscribe.js +148 -0
- package/tools/subscriptions.js +134 -0
- package/tools/suggest-tags.js +6 -8
- package/tools/tag-suggestions.js +257 -0
- package/tools/tip.js +193 -0
- package/tools/token.js +103 -0
- package/tools/update.js +1 -1
- package/tools/wallet.js +239 -186
- package/tools/watch.js +157 -0
- package/tools/webhook-test.js +388 -0
- package/tools/who.js +54 -3
- package/tools/withdraw.js +145 -0
- package/tools/work-summary.js +96 -0
- package/tools/workshop.js +327 -0
- package/tools/x-mentions.js +1 -1
- package/version.json +14 -3
- package/tools/artifacts-buy.js +0 -111
- package/tools/connect.js +0 -284
- package/tools/gigs-apply.js +0 -99
- package/tools/gigs-browse.js +0 -114
- package/tools/gigs-complete.js +0 -128
- package/tools/gigs-post.js +0 -140
- package/tools/live-off.js +0 -109
- package/tools/live-watch.js +0 -176
- package/tools/live.js +0 -128
- package/tools/sessions-browse.js +0 -105
- package/tools/whats-happening.js +0 -125
- /package/tools/{away.js → _deprecated/away.js} +0 -0
- /package/tools/{back.js → _deprecated/back.js} +0 -0
- /package/tools/{mute.js → _deprecated/mute.js} +0 -0
- /package/tools/{skills-exchange.js → _deprecated/skills-exchange.js} +0 -0
- /package/tools/{tictactoe.js → _deprecated/tictactoe.js} +0 -0
- /package/tools/{wordassociation.js → _deprecated/wordassociation.js} +0 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe webhook-test — Test webhook endpoints
|
|
3
|
+
*
|
|
4
|
+
* Send test events to webhook endpoints to verify they're working.
|
|
5
|
+
* Useful for debugging X, Telegram, Discord webhook integrations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { requireInit, header, divider, success, warning, error } = require('./_shared');
|
|
9
|
+
|
|
10
|
+
const definition = {
|
|
11
|
+
name: 'vibe_webhook_test',
|
|
12
|
+
description: 'Test webhook endpoints and view webhook activity',
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
endpoint: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
enum: ['x', 'telegram', 'discord', 'all'],
|
|
19
|
+
description: 'Which webhook to test (default: all)'
|
|
20
|
+
},
|
|
21
|
+
action: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
enum: ['test', 'status', 'stats', 'events'],
|
|
24
|
+
description: 'Test webhook, check status, view stats, or list recent events'
|
|
25
|
+
},
|
|
26
|
+
mock_event: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
enum: ['mention', 'dm', 'like', 'follow'],
|
|
29
|
+
description: 'Type of mock event to send (for testing)'
|
|
30
|
+
},
|
|
31
|
+
limit: {
|
|
32
|
+
type: 'number',
|
|
33
|
+
description: 'Number of recent events to show (default: 10)'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
async function handler(args) {
|
|
40
|
+
const initCheck = requireInit();
|
|
41
|
+
if (initCheck) return initCheck;
|
|
42
|
+
|
|
43
|
+
const {
|
|
44
|
+
endpoint = 'all',
|
|
45
|
+
action = 'status',
|
|
46
|
+
mock_event = 'mention',
|
|
47
|
+
limit = 10
|
|
48
|
+
} = args;
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
switch (action) {
|
|
52
|
+
case 'test':
|
|
53
|
+
return await testWebhook(endpoint, mock_event);
|
|
54
|
+
case 'status':
|
|
55
|
+
return await checkWebhookStatus(endpoint);
|
|
56
|
+
case 'stats':
|
|
57
|
+
return await getWebhookStats(endpoint);
|
|
58
|
+
case 'events':
|
|
59
|
+
return await getRecentEvents(endpoint, limit);
|
|
60
|
+
default:
|
|
61
|
+
return { display: error('Invalid action') };
|
|
62
|
+
}
|
|
63
|
+
} catch (e) {
|
|
64
|
+
return {
|
|
65
|
+
display: `${header('Webhook Test')}\n\n${error('Test failed: ' + e.message)}`
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function testWebhook(endpoint, mockEvent) {
|
|
71
|
+
let display = header('Webhook Test');
|
|
72
|
+
display += '\n\n';
|
|
73
|
+
|
|
74
|
+
if (endpoint === 'x' || endpoint === 'all') {
|
|
75
|
+
display += await testXWebhook(mockEvent);
|
|
76
|
+
display += '\n';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (endpoint === 'telegram' || endpoint === 'all') {
|
|
80
|
+
display += '**Telegram Webhook:** Not yet implemented\n\n';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (endpoint === 'discord' || endpoint === 'all') {
|
|
84
|
+
display += '**Discord Webhook:** Not yet implemented\n\n';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
display += divider();
|
|
88
|
+
display += '**Next steps:**\n';
|
|
89
|
+
display += '• Check webhook events: `vibe webhook-test --action events`\n';
|
|
90
|
+
display += '• View social inbox: `vibe social-inbox --include_webhooks`\n';
|
|
91
|
+
display += '• Monitor bridge health: `vibe bridge-health`';
|
|
92
|
+
|
|
93
|
+
return { display };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function testXWebhook(mockEvent) {
|
|
97
|
+
let result = '**X (Twitter) Webhook Test**\n';
|
|
98
|
+
|
|
99
|
+
// Check if endpoint exists
|
|
100
|
+
const webhookUrl = '/api/webhooks/x';
|
|
101
|
+
result += `Endpoint: ${webhookUrl}\n`;
|
|
102
|
+
|
|
103
|
+
// Check configuration
|
|
104
|
+
const secret = process.env.X_WEBHOOK_SECRET;
|
|
105
|
+
const configured = !!secret;
|
|
106
|
+
result += `Secret configured: ${configured ? '✅' : '❌'}\n`;
|
|
107
|
+
|
|
108
|
+
if (!configured) {
|
|
109
|
+
result += warning('Set X_WEBHOOK_SECRET environment variable\n');
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Create mock event
|
|
114
|
+
const mockEvents = {
|
|
115
|
+
mention: {
|
|
116
|
+
tweet_create_events: [{
|
|
117
|
+
id_str: 'test_' + Date.now(),
|
|
118
|
+
text: '@yourusername This is a test mention from webhook-test',
|
|
119
|
+
created_at: new Date().toISOString(),
|
|
120
|
+
user: {
|
|
121
|
+
id_str: 'test_user_123',
|
|
122
|
+
screen_name: 'testuser',
|
|
123
|
+
name: 'Test User',
|
|
124
|
+
profile_image_url_https: 'https://example.com/avatar.jpg'
|
|
125
|
+
},
|
|
126
|
+
entities: {
|
|
127
|
+
user_mentions: [{
|
|
128
|
+
id_str: 'your_user_id',
|
|
129
|
+
screen_name: 'yourusername'
|
|
130
|
+
}]
|
|
131
|
+
}
|
|
132
|
+
}]
|
|
133
|
+
},
|
|
134
|
+
dm: {
|
|
135
|
+
direct_message_events: [{
|
|
136
|
+
id: 'test_dm_' + Date.now(),
|
|
137
|
+
created_timestamp: Date.now().toString(),
|
|
138
|
+
message_create: {
|
|
139
|
+
sender_id: 'test_user_123',
|
|
140
|
+
target: { recipient_id: 'your_user_id' },
|
|
141
|
+
message_data: { text: 'Test DM from webhook-test tool' }
|
|
142
|
+
}
|
|
143
|
+
}]
|
|
144
|
+
},
|
|
145
|
+
like: {
|
|
146
|
+
favorite_events: [{
|
|
147
|
+
id_str: 'test_like_' + Date.now(),
|
|
148
|
+
created_at: new Date().toISOString(),
|
|
149
|
+
user: {
|
|
150
|
+
id_str: 'test_user_123',
|
|
151
|
+
screen_name: 'testuser',
|
|
152
|
+
name: 'Test User',
|
|
153
|
+
profile_image_url_https: 'https://example.com/avatar.jpg'
|
|
154
|
+
},
|
|
155
|
+
favorited_status: {
|
|
156
|
+
id_str: 'your_tweet_123',
|
|
157
|
+
text: 'Your tweet that got liked',
|
|
158
|
+
user: { id_str: 'your_user_id' }
|
|
159
|
+
}
|
|
160
|
+
}]
|
|
161
|
+
},
|
|
162
|
+
follow: {
|
|
163
|
+
follow_events: [{
|
|
164
|
+
source: {
|
|
165
|
+
id_str: 'test_user_123',
|
|
166
|
+
screen_name: 'testuser',
|
|
167
|
+
name: 'Test User',
|
|
168
|
+
profile_image_url_https: 'https://example.com/avatar.jpg',
|
|
169
|
+
description: 'A test user following you'
|
|
170
|
+
},
|
|
171
|
+
target: { id_str: 'your_user_id' },
|
|
172
|
+
created_at: new Date().toISOString()
|
|
173
|
+
}]
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const testPayload = mockEvents[mockEvent] || mockEvents.mention;
|
|
178
|
+
result += `Mock event: ${mockEvent}\n`;
|
|
179
|
+
|
|
180
|
+
// Generate test signature
|
|
181
|
+
const crypto = require('crypto');
|
|
182
|
+
const body = JSON.stringify(testPayload);
|
|
183
|
+
const signature = crypto
|
|
184
|
+
.createHmac('sha256', secret)
|
|
185
|
+
.update(body)
|
|
186
|
+
.digest('base64');
|
|
187
|
+
|
|
188
|
+
result += `Event size: ${body.length} bytes\n`;
|
|
189
|
+
result += success('Mock event ready for webhook\n');
|
|
190
|
+
|
|
191
|
+
// Note: We don't actually send the request to avoid spamming
|
|
192
|
+
// In a real implementation, this could make an HTTP request to localhost
|
|
193
|
+
result += warning('Note: This creates a mock event structure.\n');
|
|
194
|
+
result += 'To test fully, send POST to /api/webhooks/x with:\n';
|
|
195
|
+
result += `• Header: x-twitter-webhooks-signature: sha256=${signature}\n`;
|
|
196
|
+
result += `• Body: ${body.slice(0, 100)}...\n`;
|
|
197
|
+
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function checkWebhookStatus(endpoint) {
|
|
202
|
+
let display = header('Webhook Status');
|
|
203
|
+
display += '\n\n';
|
|
204
|
+
|
|
205
|
+
// X webhook status
|
|
206
|
+
if (endpoint === 'x' || endpoint === 'all') {
|
|
207
|
+
const xSecret = process.env.X_WEBHOOK_SECRET;
|
|
208
|
+
const xBearer = process.env.X_BEARER_TOKEN;
|
|
209
|
+
|
|
210
|
+
display += '**X (Twitter) Webhook**\n';
|
|
211
|
+
display += `Endpoint: /api/webhooks/x\n`;
|
|
212
|
+
display += `Secret: ${xSecret ? '✅ Configured' : '❌ Missing'}\n`;
|
|
213
|
+
display += `Bearer Token: ${xBearer ? '✅ Configured' : '❌ Missing'}\n`;
|
|
214
|
+
|
|
215
|
+
if (xSecret && xBearer) {
|
|
216
|
+
display += success('Ready for CRC challenge and events\n');
|
|
217
|
+
} else {
|
|
218
|
+
display += warning('Set X_WEBHOOK_SECRET and X_BEARER_TOKEN\n');
|
|
219
|
+
}
|
|
220
|
+
display += '\n';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// General webhook system
|
|
224
|
+
const kvConfigured = !!(process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN);
|
|
225
|
+
display += '**Storage System**\n';
|
|
226
|
+
display += `KV Store: ${kvConfigured ? '✅ Available' : '❌ Not configured'}\n`;
|
|
227
|
+
display += `Social Inbox: ${kvConfigured ? '✅ Active' : '❌ Disabled'}\n`;
|
|
228
|
+
|
|
229
|
+
if (!kvConfigured) {
|
|
230
|
+
display += warning('Set KV_REST_API_URL and KV_REST_API_TOKEN for event storage\n');
|
|
231
|
+
}
|
|
232
|
+
display += '\n';
|
|
233
|
+
|
|
234
|
+
display += divider();
|
|
235
|
+
display += '**Webhook URLs (for X Developer Portal):**\n';
|
|
236
|
+
display += '• Development: `https://yourapp.vercel.app/api/webhooks/x`\n';
|
|
237
|
+
display += '• Production: `https://vibe.fyi/api/webhooks/x`\n\n';
|
|
238
|
+
display += '**Required X Webhook Events:**\n';
|
|
239
|
+
display += '• Tweet create events (mentions)\n';
|
|
240
|
+
display += '• Direct message events\n';
|
|
241
|
+
display += '• Favorite events (optional)\n';
|
|
242
|
+
display += '• Follow events (optional)';
|
|
243
|
+
|
|
244
|
+
return { display };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function getWebhookStats(endpoint) {
|
|
248
|
+
let display = header('Webhook Statistics');
|
|
249
|
+
display += '\n\n';
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
const KV_CONFIGURED = !!(process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN);
|
|
253
|
+
if (!KV_CONFIGURED) {
|
|
254
|
+
display += warning('KV storage not configured - no stats available\n');
|
|
255
|
+
return { display };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const { kv } = await import('@vercel/kv');
|
|
259
|
+
|
|
260
|
+
// X webhook stats
|
|
261
|
+
if (endpoint === 'x' || endpoint === 'all') {
|
|
262
|
+
const xStats = await kv.hgetall('vibe:x_webhook_stats') || {};
|
|
263
|
+
|
|
264
|
+
display += '**X Webhook Stats**\n';
|
|
265
|
+
display += `Total deliveries: ${xStats.total_deliveries || 0}\n`;
|
|
266
|
+
display += `Events processed: ${xStats.events_processed || 0}\n`;
|
|
267
|
+
display += `Last delivery: ${xStats.last_delivery ? new Date(xStats.last_delivery).toLocaleString() : 'Never'}\n`;
|
|
268
|
+
|
|
269
|
+
// Daily stats (last 7 days)
|
|
270
|
+
const today = new Date();
|
|
271
|
+
const dailyStats = [];
|
|
272
|
+
for (let i = 6; i >= 0; i--) {
|
|
273
|
+
const date = new Date(today);
|
|
274
|
+
date.setDate(date.getDate() - i);
|
|
275
|
+
const dateKey = date.toISOString().split('T')[0];
|
|
276
|
+
const count = parseInt(xStats[`deliveries_${dateKey}`]) || 0;
|
|
277
|
+
if (count > 0) {
|
|
278
|
+
dailyStats.push(`${dateKey}: ${count}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (dailyStats.length > 0) {
|
|
283
|
+
display += `Daily activity (last 7d): ${dailyStats.join(', ')}\n`;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
display += '\n';
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
display += divider();
|
|
290
|
+
display += '**Health Check:**\n';
|
|
291
|
+
display += '• Test webhook: `vibe webhook-test --action test --endpoint x`\n';
|
|
292
|
+
display += '• View recent events: `vibe webhook-test --action events --limit 5`\n';
|
|
293
|
+
display += '• Check social inbox: `vibe social-inbox --channel webhooks`';
|
|
294
|
+
|
|
295
|
+
} catch (e) {
|
|
296
|
+
display += error(`Stats unavailable: ${e.message}\n`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return { display };
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async function getRecentEvents(endpoint, limit) {
|
|
303
|
+
let display = header(`Recent Webhook Events (${limit})`);
|
|
304
|
+
display += '\n\n';
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
const KV_CONFIGURED = !!(process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN);
|
|
308
|
+
if (!KV_CONFIGURED) {
|
|
309
|
+
display += warning('KV storage not configured - no events available\n');
|
|
310
|
+
return { display };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const { kv } = await import('@vercel/kv');
|
|
314
|
+
const inboxKey = 'vibe:social_inbox';
|
|
315
|
+
|
|
316
|
+
const rawEvents = await kv.lrange(inboxKey, 0, limit - 1);
|
|
317
|
+
|
|
318
|
+
if (rawEvents.length === 0) {
|
|
319
|
+
display += '_No recent webhook events._\n\n';
|
|
320
|
+
display += 'Events will appear here when webhooks are triggered.\n';
|
|
321
|
+
display += 'Test with: `vibe webhook-test --action test --endpoint x`';
|
|
322
|
+
return { display };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
display += `Found ${rawEvents.length} recent events:\n`;
|
|
326
|
+
display += divider();
|
|
327
|
+
display += '\n';
|
|
328
|
+
|
|
329
|
+
for (const eventStr of rawEvents) {
|
|
330
|
+
try {
|
|
331
|
+
const event = JSON.parse(eventStr);
|
|
332
|
+
const timeAgo = formatTimeAgo(new Date(event.timestamp));
|
|
333
|
+
|
|
334
|
+
display += `**${event.platform.toUpperCase()}** ${getTypeIcon(event.type)} — _${timeAgo}_\n`;
|
|
335
|
+
display += `From: @${event.from.handle} (${event.from.name})\n`;
|
|
336
|
+
display += `${event.content.slice(0, 100)}${event.content.length > 100 ? '...' : ''}\n`;
|
|
337
|
+
|
|
338
|
+
if (event.metadata?.url) {
|
|
339
|
+
display += `Link: ${event.metadata.url}\n`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
display += `Status: ${event.processed ? '✅ Processed' : '⏳ Pending'}\n`;
|
|
343
|
+
display += `_[${event.id}]_\n\n`;
|
|
344
|
+
|
|
345
|
+
} catch (e) {
|
|
346
|
+
display += `_Invalid event data_\n\n`;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
display += divider();
|
|
351
|
+
display += '**Actions:**\n';
|
|
352
|
+
display += '• View in social inbox: `vibe social-inbox --include_webhooks`\n';
|
|
353
|
+
display += '• Reply to event: `vibe social-post --content "reply"`\n';
|
|
354
|
+
display += '• Clear events: `vibe webhook-test --action clear` (not implemented)';
|
|
355
|
+
|
|
356
|
+
} catch (e) {
|
|
357
|
+
display += error(`Failed to fetch events: ${e.message}\n`);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return { display };
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function formatTimeAgo(timestamp) {
|
|
364
|
+
const now = Date.now();
|
|
365
|
+
const diff = now - timestamp;
|
|
366
|
+
const minutes = Math.floor(diff / (1000 * 60));
|
|
367
|
+
const hours = Math.floor(minutes / 60);
|
|
368
|
+
const days = Math.floor(hours / 24);
|
|
369
|
+
|
|
370
|
+
if (days > 0) return `${days}d ago`;
|
|
371
|
+
if (hours > 0) return `${hours}h ago`;
|
|
372
|
+
if (minutes > 0) return `${minutes}m ago`;
|
|
373
|
+
return 'just now';
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function getTypeIcon(type) {
|
|
377
|
+
const icons = {
|
|
378
|
+
mention: '@',
|
|
379
|
+
reply: '↩️',
|
|
380
|
+
dm: '✉️',
|
|
381
|
+
like: '❤️',
|
|
382
|
+
repost: '🔁',
|
|
383
|
+
follow: '👤'
|
|
384
|
+
};
|
|
385
|
+
return icons[type] || '📡';
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
module.exports = { definition, handler };
|
package/tools/who.js
CHANGED
|
@@ -24,7 +24,7 @@ const definition = {
|
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
// Activity heat based on session signals
|
|
27
|
+
// Activity heat based on session signals + GitHub activity
|
|
28
28
|
function getHeat(user) {
|
|
29
29
|
const lastSeenMs = user.lastSeen;
|
|
30
30
|
const now = Date.now();
|
|
@@ -38,6 +38,16 @@ function getHeat(user) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// GitHub activity signals — real commits are high-confidence
|
|
42
|
+
if (user.github?.shipping_mode === 'hot') {
|
|
43
|
+
const commits = user.github.total_commits || 0;
|
|
44
|
+
const label = commits > 0 ? `shipping code (${commits} commits)` : 'shipping code';
|
|
45
|
+
return { icon: '🔥', label, inferred: false, source: 'github' };
|
|
46
|
+
}
|
|
47
|
+
if (user.github?.shipping_mode === 'active') {
|
|
48
|
+
return { icon: '⚡', label: 'pushing commits', inferred: false, source: 'github' };
|
|
49
|
+
}
|
|
50
|
+
|
|
41
51
|
// Check for inferred state from smart detection
|
|
42
52
|
if (user.mood_inferred && user.mood) {
|
|
43
53
|
const inferredLabel = user.inferred_state
|
|
@@ -73,6 +83,11 @@ function getHeat(user) {
|
|
|
73
83
|
return { icon: '🔥', label: 'shipping', inferred: false };
|
|
74
84
|
}
|
|
75
85
|
|
|
86
|
+
// GitHub building mode (lower activity but still coding)
|
|
87
|
+
if (user.github?.shipping_mode === 'building') {
|
|
88
|
+
return { icon: '🔨', label: 'building', inferred: false, source: 'github' };
|
|
89
|
+
}
|
|
90
|
+
|
|
76
91
|
// Default based on recency
|
|
77
92
|
if (minutesAgo < 2) {
|
|
78
93
|
return { icon: '⚡', label: 'active', inferred: false };
|
|
@@ -116,6 +131,13 @@ function formatActivity(user) {
|
|
|
116
131
|
return parts.join(' ');
|
|
117
132
|
}
|
|
118
133
|
|
|
134
|
+
// GitHub active repos (if no other context and GitHub connected)
|
|
135
|
+
if (user.github?.active_repos?.length > 0) {
|
|
136
|
+
const repos = user.github.active_repos.slice(0, 2);
|
|
137
|
+
const repoNames = repos.map(r => r.split('/').pop()); // Get just repo name
|
|
138
|
+
return `pushing to ${repoNames.join(', ')}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
119
141
|
// Fall back to one_liner
|
|
120
142
|
return user.one_liner || 'Building something';
|
|
121
143
|
}
|
|
@@ -128,6 +150,19 @@ async function handler(args) {
|
|
|
128
150
|
// Apply smart detection — infer states from context signals
|
|
129
151
|
const users = enhanceUsersWithInference(rawUsers);
|
|
130
152
|
const myHandle = config.getHandle();
|
|
153
|
+
const apiUrl = config.getApiUrl();
|
|
154
|
+
|
|
155
|
+
// Fetch active help requests (non-blocking)
|
|
156
|
+
let helpRequests = [];
|
|
157
|
+
try {
|
|
158
|
+
const helpResponse = await fetch(`${apiUrl}/api/help?limit=5`);
|
|
159
|
+
const helpData = await helpResponse.json();
|
|
160
|
+
if (helpData.success && helpData.requests) {
|
|
161
|
+
helpRequests = helpData.requests;
|
|
162
|
+
}
|
|
163
|
+
} catch (e) {
|
|
164
|
+
// Silent fail - help requests are nice-to-have
|
|
165
|
+
}
|
|
131
166
|
|
|
132
167
|
// Check for notifications (presence + messages)
|
|
133
168
|
notify.checkAll(store);
|
|
@@ -139,7 +174,7 @@ async function handler(args) {
|
|
|
139
174
|
_You're the only one here right now..._
|
|
140
175
|
|
|
141
176
|
🎮 **Challenge someone later**: "play tictactoe with @friend"
|
|
142
|
-
💬 **
|
|
177
|
+
💬 **Message @vibe**: Say hello to the platform!
|
|
143
178
|
🔗 **Invite a friend**: Share \`slashvibe.dev\`
|
|
144
179
|
|
|
145
180
|
_Check back in a bit — builders come and go._`
|
|
@@ -155,6 +190,17 @@ _Check back in a bit — builders come and go._`
|
|
|
155
190
|
|
|
156
191
|
let display = `## Who's Around\n\n`;
|
|
157
192
|
|
|
193
|
+
// Help requests section (if any)
|
|
194
|
+
if (helpRequests.length > 0) {
|
|
195
|
+
display += `**🆘 Needs Help:**\n`;
|
|
196
|
+
for (const req of helpRequests.slice(0, 3)) {
|
|
197
|
+
const urgencyIcon = req.urgency === 'high' ? '🔴' : req.urgency === 'low' ? '🟢' : '🟡';
|
|
198
|
+
const shortProblem = req.problem.length > 50 ? req.problem.slice(0, 47) + '...' : req.problem;
|
|
199
|
+
display += `${urgencyIcon} **@${req.handle}**: ${shortProblem}\n`;
|
|
200
|
+
}
|
|
201
|
+
display += `→ \`vibe stuck\` to help or ask\n\n---\n\n`;
|
|
202
|
+
}
|
|
203
|
+
|
|
158
204
|
// Activity section for active users
|
|
159
205
|
if (active.length > 0) {
|
|
160
206
|
active.forEach(u => {
|
|
@@ -168,7 +214,12 @@ _Check back in a bit — builders come and go._`
|
|
|
168
214
|
const activity = formatActivity(u);
|
|
169
215
|
const timeAgo = formatTimeAgo(u.lastSeen);
|
|
170
216
|
|
|
171
|
-
|
|
217
|
+
// Show availability topics if set
|
|
218
|
+
const availableFor = u.availableFor && Array.isArray(u.availableFor) && u.availableFor.length > 0
|
|
219
|
+
? ` — 🤝 _available for: ${u.availableFor.join(', ')}_`
|
|
220
|
+
: '';
|
|
221
|
+
|
|
222
|
+
display += `${heat.icon} **@${u.handle}**${agentBadge}${tag}${heatLabel}${availableFor}\n`;
|
|
172
223
|
if (operatorTag) {
|
|
173
224
|
display += ` ${operatorTag}\n`;
|
|
174
225
|
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe_withdraw - Withdraw earnings from FeeRouter
|
|
3
|
+
*
|
|
4
|
+
* Generates transaction data to withdraw accumulated on-chain earnings.
|
|
5
|
+
* User must sign and submit the transaction with their wallet.
|
|
6
|
+
*
|
|
7
|
+
* Examples:
|
|
8
|
+
* - "vibe withdraw"
|
|
9
|
+
* - "withdraw my earnings"
|
|
10
|
+
* - "claim my on-chain balance"
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fetch = require('node-fetch');
|
|
14
|
+
const config = require('../config');
|
|
15
|
+
|
|
16
|
+
const definition = {
|
|
17
|
+
name: 'vibe_withdraw',
|
|
18
|
+
description: 'Withdraw accumulated earnings from the FeeRouter. Generates transaction data for you to sign.',
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
to_address: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Optional destination address (defaults to your registered payout wallet)'
|
|
25
|
+
},
|
|
26
|
+
check_only: {
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
description: 'Only check balance without generating withdrawal transaction'
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
async function handler(args) {
|
|
35
|
+
const { to_address, check_only = false } = args;
|
|
36
|
+
|
|
37
|
+
if (!config.isInitialized()) {
|
|
38
|
+
return {
|
|
39
|
+
display: 'Run `vibe init` first to set your identity.'
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const handle = config.getHandle();
|
|
44
|
+
const token = config.getToken();
|
|
45
|
+
const apiUrl = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
// Check-only mode
|
|
49
|
+
if (check_only) {
|
|
50
|
+
const response = await fetch(
|
|
51
|
+
`${apiUrl}/api/earnings/withdraw?handle=${encodeURIComponent(handle)}`,
|
|
52
|
+
{
|
|
53
|
+
headers: token ? {
|
|
54
|
+
'Authorization': `Bearer ${token}`
|
|
55
|
+
} : {}
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const result = await response.json();
|
|
60
|
+
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
return {
|
|
63
|
+
display: `❌ ${result.error || 'Failed to check balance'}`
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const statusEmoji = result.can_withdraw ? '🟢' : '🟡';
|
|
68
|
+
const action = result.can_withdraw
|
|
69
|
+
? 'Run "vibe withdraw" to generate withdrawal transaction.'
|
|
70
|
+
: result.registered
|
|
71
|
+
? 'No balance to withdraw yet.'
|
|
72
|
+
: 'Register on-chain first: vibe register_onchain';
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
display: `
|
|
76
|
+
💰 Withdrawal Status: @${handle}
|
|
77
|
+
|
|
78
|
+
Balance: ${result.balance_eth} ETH
|
|
79
|
+
Registered: ${result.registered ? 'Yes' : 'No'}
|
|
80
|
+
Can Withdraw: ${statusEmoji} ${result.can_withdraw ? 'Yes' : 'No'}
|
|
81
|
+
|
|
82
|
+
${action}
|
|
83
|
+
`.trim(),
|
|
84
|
+
data: result
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Generate withdrawal transaction
|
|
89
|
+
const response = await fetch(
|
|
90
|
+
`${apiUrl}/api/earnings/withdraw`,
|
|
91
|
+
{
|
|
92
|
+
method: 'POST',
|
|
93
|
+
headers: {
|
|
94
|
+
'Content-Type': 'application/json',
|
|
95
|
+
'Authorization': `Bearer ${token}`
|
|
96
|
+
},
|
|
97
|
+
body: JSON.stringify({
|
|
98
|
+
to_address: to_address || undefined
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const result = await response.json();
|
|
104
|
+
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
let display = `❌ ${result.error || 'Failed to generate withdrawal'}`;
|
|
107
|
+
if (result.action) {
|
|
108
|
+
display += `\n\nNext step: ${result.action}`;
|
|
109
|
+
}
|
|
110
|
+
return { display };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const destDisplay = result.destination
|
|
114
|
+
? `${result.destination.slice(0, 10)}...${result.destination.slice(-8)}`
|
|
115
|
+
: 'your wallet';
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
display: `
|
|
119
|
+
💸 Withdrawal Ready: @${handle}
|
|
120
|
+
|
|
121
|
+
Amount: ${result.balance_display}
|
|
122
|
+
Destination: ${destDisplay}
|
|
123
|
+
Network: ${result.network}
|
|
124
|
+
|
|
125
|
+
Transaction to sign:
|
|
126
|
+
To: ${result.transaction.to}
|
|
127
|
+
Data: ${result.transaction.data?.slice(0, 40)}...
|
|
128
|
+
Gas: ${result.transaction.gas}
|
|
129
|
+
|
|
130
|
+
Instructions:
|
|
131
|
+
${result.instructions?.map((i, idx) => ` ${idx + 1}. ${i}`).join('\n')}
|
|
132
|
+
|
|
133
|
+
Explorer: ${result.explorer}
|
|
134
|
+
`.trim(),
|
|
135
|
+
data: result
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
} catch (error) {
|
|
139
|
+
return {
|
|
140
|
+
display: `❌ Failed to process withdrawal: ${error.message}`
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports = { definition, handler };
|