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
package/tools/dm.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* vibe dm — Send a direct message
|
|
3
|
-
* Supports internal /vibe DMs and external platforms (Gmail, X, Farcaster, etc.)
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
const config = require('../config');
|
|
@@ -9,13 +8,12 @@ const memory = require('../memory');
|
|
|
9
8
|
const userProfiles = require('../store/profiles');
|
|
10
9
|
const patterns = require('../intelligence/patterns');
|
|
11
10
|
const { trackMessage, checkBurst } = require('./summarize');
|
|
12
|
-
const { requireInit, normalizeHandle, truncate, warning } = require('./_shared');
|
|
11
|
+
const { requireInit, normalizeHandle, truncate, warning, fetchRelevantUsers } = require('./_shared');
|
|
13
12
|
const { actions, formatActions } = require('./_actions');
|
|
14
|
-
const router = require('../../lib/messaging/router');
|
|
15
13
|
|
|
16
14
|
const definition = {
|
|
17
15
|
name: 'vibe_dm',
|
|
18
|
-
description: 'Send a direct message to someone. Can include structured payload for games, handoffs,
|
|
16
|
+
description: 'Send a direct message to someone. Can include structured payload for games, handoffs, artifact cards, or an instant USDC tip.',
|
|
19
17
|
inputSchema: {
|
|
20
18
|
type: 'object',
|
|
21
19
|
properties: {
|
|
@@ -27,9 +25,21 @@ const definition = {
|
|
|
27
25
|
type: 'string',
|
|
28
26
|
description: 'Your message'
|
|
29
27
|
},
|
|
28
|
+
artifact_slug: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
description: 'Optional artifact slug to share (e.g., "pizza-guide-abc123"). The artifact will be shown as a rich card.'
|
|
31
|
+
},
|
|
30
32
|
payload: {
|
|
31
33
|
type: 'object',
|
|
32
34
|
description: 'Optional structured data (game state, code review, handoff, etc.)'
|
|
35
|
+
},
|
|
36
|
+
reply_to: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: 'Optional: Message ID to reply to (creates a threaded reply)'
|
|
39
|
+
},
|
|
40
|
+
tip_amount_cents: {
|
|
41
|
+
type: 'number',
|
|
42
|
+
description: 'Optional: Attach an instant USDC tip (100 = $1, 500 = $5, 1000 = $10)'
|
|
33
43
|
}
|
|
34
44
|
},
|
|
35
45
|
required: ['handle']
|
|
@@ -40,7 +50,7 @@ async function handler(args) {
|
|
|
40
50
|
const initCheck = requireInit();
|
|
41
51
|
if (initCheck) return initCheck;
|
|
42
52
|
|
|
43
|
-
const { handle, message, payload } = args;
|
|
53
|
+
const { handle, message, artifact_slug, payload, reply_to, tip_amount_cents } = args;
|
|
44
54
|
const myHandle = config.getHandle();
|
|
45
55
|
const them = normalizeHandle(handle);
|
|
46
56
|
|
|
@@ -54,82 +64,51 @@ async function handler(args) {
|
|
|
54
64
|
return { display: 'You can\'t DM yourself.' };
|
|
55
65
|
}
|
|
56
66
|
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Check if this is an external platform message (email, X, Farcaster, etc.)
|
|
63
|
-
const platform = router.detectPlatform(handle);
|
|
64
|
-
|
|
65
|
-
if (platform !== 'vibe') {
|
|
66
|
-
// External platform - route via messaging adapters
|
|
67
|
+
// Handle artifact sharing
|
|
68
|
+
let finalPayload = payload;
|
|
69
|
+
if (artifact_slug) {
|
|
67
70
|
try {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
platform: args.platform // Allow explicit platform override
|
|
72
|
-
});
|
|
71
|
+
// Fetch artifact from API
|
|
72
|
+
const { getArtifactBySlug } = require('./artifact-view');
|
|
73
|
+
const artifact = await getArtifactBySlug(artifact_slug);
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return {
|
|
77
|
-
display: `🔐 Connect ${platform} first
|
|
78
|
-
|
|
79
|
-
${platform} is not connected yet. Connect it to send messages:
|
|
80
|
-
|
|
81
|
-
vibe connect ${platform}
|
|
82
|
-
|
|
83
|
-
Then you can message:
|
|
84
|
-
vibe dm ${handle} "${message.slice(0, 50)}..."
|
|
85
|
-
`
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Success
|
|
90
|
-
if (result.success) {
|
|
91
|
-
return {
|
|
92
|
-
display: `✓ Sent via ${platform}
|
|
93
|
-
|
|
94
|
-
To: ${handle}
|
|
95
|
-
Message: "${truncate(message, 100)}"
|
|
96
|
-
|
|
97
|
-
${platform === 'gmail' ? `Message ID: ${result.messageId}` : ''}
|
|
98
|
-
`
|
|
99
|
-
};
|
|
75
|
+
if (!artifact) {
|
|
76
|
+
return { display: `Artifact not found: ${artifact_slug}` };
|
|
100
77
|
}
|
|
101
78
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
Error: ${result.error}
|
|
107
|
-
|
|
108
|
-
${platform} may need to be reconnected:
|
|
109
|
-
vibe connect ${platform} --action status
|
|
110
|
-
`
|
|
111
|
-
};
|
|
79
|
+
// Create artifact payload
|
|
80
|
+
const protocol = require('../protocol');
|
|
81
|
+
finalPayload = protocol.createArtifactPayload(artifact);
|
|
112
82
|
} catch (error) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
${error.message}
|
|
117
|
-
|
|
118
|
-
Make sure ${platform} is configured:
|
|
119
|
-
vibe connect ${platform}
|
|
120
|
-
`
|
|
121
|
-
};
|
|
83
|
+
console.error('Failed to load artifact:', error);
|
|
84
|
+
return { display: `Failed to load artifact: ${error.message}` };
|
|
122
85
|
}
|
|
123
86
|
}
|
|
124
87
|
|
|
125
|
-
//
|
|
88
|
+
// Need either message or payload
|
|
89
|
+
if ((!message || message.trim().length === 0) && !finalPayload) {
|
|
90
|
+
return { display: 'Need either a message, artifact, or payload.' };
|
|
91
|
+
}
|
|
126
92
|
|
|
127
93
|
const trimmed = message ? message.trim() : '';
|
|
128
94
|
const MAX_LENGTH = 2000;
|
|
129
95
|
const wasTruncated = trimmed.length > MAX_LENGTH;
|
|
130
96
|
const finalMessage = wasTruncated ? trimmed.substring(0, MAX_LENGTH) : trimmed;
|
|
131
97
|
|
|
132
|
-
|
|
98
|
+
// Send typing indicator (shows "typing..." to recipient while message is being sent)
|
|
99
|
+
// Non-blocking - we don't wait for this
|
|
100
|
+
store.sendTypingIndicator(myHandle, them).catch(() => {});
|
|
101
|
+
|
|
102
|
+
const result = await store.sendMessage(myHandle, them, finalMessage || null, 'dm', finalPayload, {
|
|
103
|
+
replyTo: reply_to || null,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Check for errors
|
|
107
|
+
if (result && result.error) {
|
|
108
|
+
return {
|
|
109
|
+
display: `❌ **Failed to send message**\n\n${result.message}\n\n_Please try again. If the problem persists, check your connection._`
|
|
110
|
+
};
|
|
111
|
+
}
|
|
133
112
|
|
|
134
113
|
// Log social pattern (quietly, in background)
|
|
135
114
|
patterns.logMessageSent(them);
|
|
@@ -156,13 +135,56 @@ Make sure ${platform} is configured:
|
|
|
156
135
|
display += ` ${warning(`truncated to ${MAX_LENGTH} chars`)}`;
|
|
157
136
|
}
|
|
158
137
|
|
|
159
|
-
//
|
|
160
|
-
if (
|
|
161
|
-
|
|
138
|
+
// Only show payload type indicator (message already visible in tool call)
|
|
139
|
+
if (finalPayload) {
|
|
140
|
+
const payloadType = finalPayload.type || 'data';
|
|
141
|
+
if (payloadType === 'artifact') {
|
|
142
|
+
const icon = finalPayload.template === 'guide' ? '📘' : finalPayload.template === 'learning' ? '💡' : finalPayload.template === 'workspace' ? '🗂️' : '📦';
|
|
143
|
+
display += `\n\n${icon} _Shared artifact: ${finalPayload.title}_`;
|
|
144
|
+
} else {
|
|
145
|
+
display += `\n\n📦 _Includes ${payloadType} payload_`;
|
|
146
|
+
}
|
|
162
147
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
148
|
+
|
|
149
|
+
// Execute attached tip if specified
|
|
150
|
+
let tipResult = null;
|
|
151
|
+
if (tip_amount_cents && tip_amount_cents > 0) {
|
|
152
|
+
const token = config.getToken();
|
|
153
|
+
if (token) {
|
|
154
|
+
try {
|
|
155
|
+
const apiUrl = config.getApiUrl();
|
|
156
|
+
// Generate idempotency key to prevent duplicate tips from retries
|
|
157
|
+
const timeBucket = Math.floor(Date.now() / 60000);
|
|
158
|
+
const tipIdempotencyKey = `dm:${myHandle}:${them}:${tip_amount_cents}:${timeBucket}`;
|
|
159
|
+
|
|
160
|
+
const tipResponse = await fetch(`${apiUrl}/api/tips/instant`, {
|
|
161
|
+
method: 'POST',
|
|
162
|
+
headers: {
|
|
163
|
+
'Content-Type': 'application/json',
|
|
164
|
+
'Authorization': `Bearer ${token}`,
|
|
165
|
+
'Idempotency-Key': tipIdempotencyKey
|
|
166
|
+
},
|
|
167
|
+
body: JSON.stringify({
|
|
168
|
+
to: them,
|
|
169
|
+
amount_cents: tip_amount_cents,
|
|
170
|
+
message: message ? `${message.substring(0, 50)}...` : null,
|
|
171
|
+
context: { type: 'dm_reply' }
|
|
172
|
+
})
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
tipResult = await tipResponse.json();
|
|
176
|
+
|
|
177
|
+
if (tipResult.success) {
|
|
178
|
+
const tipAmount = (tip_amount_cents / 100).toFixed(0);
|
|
179
|
+
display += `\n\n💸 _Tipped $${tipAmount} USDC — [view tx](${tipResult.explorer_url})_`;
|
|
180
|
+
} else {
|
|
181
|
+
display += `\n\n⚠️ _Tip failed: ${tipResult.message || 'Unknown error'}_`;
|
|
182
|
+
}
|
|
183
|
+
} catch (tipError) {
|
|
184
|
+
console.warn('[dm] Tip execution failed:', tipError.message);
|
|
185
|
+
display += `\n\n⚠️ _Tip failed: ${tipError.message}_`;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
166
188
|
}
|
|
167
189
|
|
|
168
190
|
// Burst notification (5+ messages in one thread)
|
|
@@ -192,6 +214,26 @@ Make sure ${platform} is configured:
|
|
|
192
214
|
// Add guided mode actions
|
|
193
215
|
response.actions = formatActions(actions.afterDm(them));
|
|
194
216
|
|
|
217
|
+
// Fetch DM suggestions (async, non-blocking for response)
|
|
218
|
+
// This adds "You might want to message..." suggestions
|
|
219
|
+
try {
|
|
220
|
+
const suggestions = await fetchRelevantUsers(myHandle, 'dm_suggest', 3);
|
|
221
|
+
if (suggestions && suggestions.matches && suggestions.matches.length > 0) {
|
|
222
|
+
// Filter out the person we just messaged
|
|
223
|
+
const others = suggestions.matches.filter(m => m.handle !== them);
|
|
224
|
+
if (others.length > 0) {
|
|
225
|
+
response.dm_suggestions = others.map(m => ({
|
|
226
|
+
handle: m.handle,
|
|
227
|
+
building: m.building,
|
|
228
|
+
reasons: m.reasons?.slice(0, 2) || []
|
|
229
|
+
}));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
} catch (e) {
|
|
233
|
+
// Don't fail DM if suggestions fail
|
|
234
|
+
console.log('[dm] dm_suggest fetch error:', e.message);
|
|
235
|
+
}
|
|
236
|
+
|
|
195
237
|
return response;
|
|
196
238
|
}
|
|
197
239
|
|
package/tools/drawing.js
CHANGED
|
@@ -86,7 +86,7 @@ async function saveDrawingState(room, state) {
|
|
|
86
86
|
* Post drawing activity to board
|
|
87
87
|
*/
|
|
88
88
|
async function postDrawingActivity(action, room, player, details = '') {
|
|
89
|
-
const API_URL = process.env.VIBE_API_URL || 'https://slashvibe.dev';
|
|
89
|
+
const API_URL = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
|
|
90
90
|
|
|
91
91
|
try {
|
|
92
92
|
let content;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe_earnings - View unified earnings dashboard
|
|
3
|
+
*
|
|
4
|
+
* Shows all revenue sources aggregated:
|
|
5
|
+
* - Artifact sales
|
|
6
|
+
* - Session PPV revenue
|
|
7
|
+
* - Subscription revenue
|
|
8
|
+
* - Gig completions
|
|
9
|
+
* - Tips received
|
|
10
|
+
* - On-chain balance
|
|
11
|
+
*
|
|
12
|
+
* Examples:
|
|
13
|
+
* - "vibe earnings"
|
|
14
|
+
* - "show my earnings"
|
|
15
|
+
* - "how much have I made"
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fetch = require('node-fetch');
|
|
19
|
+
const config = require('../config');
|
|
20
|
+
|
|
21
|
+
const definition = {
|
|
22
|
+
name: 'vibe_earnings',
|
|
23
|
+
description: 'View your unified earnings dashboard - artifact sales, session PPV, subscriptions, gigs, tips, and on-chain balance.',
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
handle: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: 'Handle to check earnings for (defaults to your handle)'
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
async function handler(args) {
|
|
36
|
+
const { handle } = args;
|
|
37
|
+
|
|
38
|
+
if (!config.isInitialized()) {
|
|
39
|
+
return {
|
|
40
|
+
display: 'Run `vibe init` first to set your identity.'
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const targetHandle = handle || config.getHandle();
|
|
45
|
+
const apiUrl = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
|
|
46
|
+
const token = config.getToken();
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const response = await fetch(
|
|
50
|
+
`${apiUrl}/api/earnings?handle=${encodeURIComponent(targetHandle)}`,
|
|
51
|
+
{
|
|
52
|
+
headers: token ? {
|
|
53
|
+
'Authorization': `Bearer ${token}`
|
|
54
|
+
} : {}
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const result = await response.json();
|
|
59
|
+
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
return {
|
|
62
|
+
display: `❌ ${result.error || 'Failed to fetch earnings'}`
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const { earnings, total_earned_display, pending_display, on_chain } = result;
|
|
67
|
+
|
|
68
|
+
let formatted = `
|
|
69
|
+
┌────────────────────────────────────────────────────────────┐
|
|
70
|
+
│ 💰 EARNINGS DASHBOARD │
|
|
71
|
+
│ @${targetHandle.padEnd(33)}│
|
|
72
|
+
├────────────────────────────────────────────────────────────┤
|
|
73
|
+
│ │
|
|
74
|
+
│ Total Earned: ${(total_earned_display || '$0').padEnd(40)} │
|
|
75
|
+
│ Pending (On-chain): ${(pending_display || '$0').padEnd(34)} │
|
|
76
|
+
│ │
|
|
77
|
+
├────────────────────────────────────────────────────────────┤
|
|
78
|
+
│ 📊 Breakdown by Source: │
|
|
79
|
+
│ │
|
|
80
|
+
│ Artifacts: ${(earnings.artifacts.revenue_display || '$0').padEnd(15)} (${earnings.artifacts.sales || 0} sales)${' '.repeat(17)} │
|
|
81
|
+
│ Sessions: ${(earnings.sessions.revenue_display || '$0').padEnd(15)} (${earnings.sessions.ppv_sales || 0} PPV)${' '.repeat(18)} │
|
|
82
|
+
│ Gigs: ${(earnings.gigs.revenue_display || '$0').padEnd(15)} (${earnings.gigs.completed || 0} completed)${' '.repeat(12)} │
|
|
83
|
+
│ Tips: ${(earnings.tips.revenue_display || '$0').padEnd(15)} (${earnings.tips.received || 0} received)${' '.repeat(13)} │
|
|
84
|
+
│ │
|
|
85
|
+
`.trim();
|
|
86
|
+
|
|
87
|
+
if (on_chain) {
|
|
88
|
+
formatted += `
|
|
89
|
+
├────────────────────────────────────────────────────────────┤
|
|
90
|
+
│ ⛓️ On-Chain Status: │`;
|
|
91
|
+
|
|
92
|
+
if (on_chain.registered) {
|
|
93
|
+
const walletDisplay = on_chain.wallet
|
|
94
|
+
? `${on_chain.wallet.slice(0, 10)}...${on_chain.wallet.slice(-8)}`
|
|
95
|
+
: 'not set';
|
|
96
|
+
formatted += `
|
|
97
|
+
│ │
|
|
98
|
+
│ Registered: Yes │
|
|
99
|
+
│ Wallet: ${walletDisplay.padEnd(47)} │
|
|
100
|
+
│ Balance: ${(on_chain.balance_eth + ' ETH').padEnd(46)} │`;
|
|
101
|
+
if (on_chain.can_withdraw) {
|
|
102
|
+
formatted += `
|
|
103
|
+
│ ✅ Ready to withdraw │`;
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
formatted += `
|
|
107
|
+
│ │
|
|
108
|
+
│ Registered: No │
|
|
109
|
+
│ ⚠️ Register to receive payments: vibe register_onchain │`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
formatted += `
|
|
114
|
+
│ │
|
|
115
|
+
└────────────────────────────────────────────────────────────┘`;
|
|
116
|
+
|
|
117
|
+
return { display: formatted, data: result };
|
|
118
|
+
|
|
119
|
+
} catch (error) {
|
|
120
|
+
return {
|
|
121
|
+
display: `❌ Failed to fetch earnings: ${error.message}`
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = { definition, handler };
|
package/tools/echo.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
const config = require('../config');
|
|
14
14
|
const { formatTimeAgo, requireInit } = require('./_shared');
|
|
15
|
+
const store = require('../store');
|
|
15
16
|
|
|
16
17
|
const API_URL = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
|
|
17
18
|
|
|
@@ -186,6 +187,21 @@ async function handler(args) {
|
|
|
186
187
|
return { display };
|
|
187
188
|
}
|
|
188
189
|
|
|
190
|
+
// Track feedback for onboarding checklist
|
|
191
|
+
try {
|
|
192
|
+
await fetch(`${API_URL}/api/onboarding/checklist`, {
|
|
193
|
+
method: 'POST',
|
|
194
|
+
headers: { 'Content-Type': 'application/json' },
|
|
195
|
+
body: JSON.stringify({
|
|
196
|
+
handle: myHandle,
|
|
197
|
+
taskId: 'leave_feedback',
|
|
198
|
+
metadata: { anonymous }
|
|
199
|
+
})
|
|
200
|
+
});
|
|
201
|
+
} catch (e) {
|
|
202
|
+
// Non-critical, continue
|
|
203
|
+
}
|
|
204
|
+
|
|
189
205
|
if (anonymous) {
|
|
190
206
|
display += pickRandom(responses.receivedAnon);
|
|
191
207
|
} else {
|
package/tools/feed.js
CHANGED
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
const config = require('../config');
|
|
16
|
-
const { requireInit, header, emptyState, formatTimeAgo, divider } = require('./_shared');
|
|
16
|
+
const { requireInit, header, emptyState, formatTimeAgo, divider, fetchRelevantUsers } = require('./_shared');
|
|
17
|
+
const { actions, formatActions } = require('./_actions');
|
|
17
18
|
|
|
18
19
|
const definition = {
|
|
19
20
|
name: 'vibe_feed',
|
|
@@ -23,7 +24,7 @@ const definition = {
|
|
|
23
24
|
properties: {
|
|
24
25
|
filter: {
|
|
25
26
|
type: 'string',
|
|
26
|
-
enum: ['all', 'ideas', 'ships', 'requests', 'riffs'],
|
|
27
|
+
enum: ['all', 'ideas', 'ships', 'requests', 'riffs', 'observations'],
|
|
27
28
|
description: 'Filter by type (default: all)'
|
|
28
29
|
},
|
|
29
30
|
from: {
|
|
@@ -48,6 +49,7 @@ const TYPE_CONFIG = {
|
|
|
48
49
|
'shipped': { emoji: '🚀', label: 'ship', verb: 'shipped' },
|
|
49
50
|
'request': { emoji: '🔓', label: 'request', verb: 'requested' },
|
|
50
51
|
'claim': { emoji: '🔨', label: 'claim', verb: 'claimed' },
|
|
52
|
+
'observation': { emoji: '👁️', label: 'observation', verb: 'observed' },
|
|
51
53
|
'general': { emoji: '📝', label: 'post', verb: 'posted' }
|
|
52
54
|
};
|
|
53
55
|
|
|
@@ -55,7 +57,8 @@ const CATEGORY_MAP = {
|
|
|
55
57
|
'ideas': ['idea', 'riff'],
|
|
56
58
|
'ships': ['shipped'],
|
|
57
59
|
'requests': ['request', 'claim'],
|
|
58
|
-
'riffs': ['riff']
|
|
60
|
+
'riffs': ['riff'],
|
|
61
|
+
'observations': ['observation']
|
|
59
62
|
};
|
|
60
63
|
|
|
61
64
|
async function handler(args) {
|
|
@@ -98,8 +101,27 @@ async function handler(args) {
|
|
|
98
101
|
);
|
|
99
102
|
}
|
|
100
103
|
|
|
101
|
-
//
|
|
102
|
-
|
|
104
|
+
// Fetch relevant users to personalize feed
|
|
105
|
+
// Content from relevant users gets a time boost (appears more recent)
|
|
106
|
+
const relevantHandles = new Set();
|
|
107
|
+
try {
|
|
108
|
+
const relevancy = await fetchRelevantUsers(myHandle, 'feed', 20);
|
|
109
|
+
if (relevancy && relevancy.matches) {
|
|
110
|
+
relevancy.matches.forEach(m => relevantHandles.add(m.handle.toLowerCase()));
|
|
111
|
+
}
|
|
112
|
+
} catch (e) {
|
|
113
|
+
// Don't fail feed if relevancy fails
|
|
114
|
+
console.log('[feed] relevancy fetch error:', e.message);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Sort by timestamp with relevancy boost
|
|
118
|
+
// Relevant users get a 6-hour boost (their content appears "more recent")
|
|
119
|
+
const RELEVANCY_BOOST_MS = 6 * 60 * 60 * 1000; // 6 hours in ms
|
|
120
|
+
entries.sort((a, b) => {
|
|
121
|
+
const aBoost = relevantHandles.has(a.author.toLowerCase()) ? RELEVANCY_BOOST_MS : 0;
|
|
122
|
+
const bBoost = relevantHandles.has(b.author.toLowerCase()) ? RELEVANCY_BOOST_MS : 0;
|
|
123
|
+
return (b.timestamp + bBoost) - (a.timestamp + aBoost);
|
|
124
|
+
});
|
|
103
125
|
|
|
104
126
|
// Limit
|
|
105
127
|
entries = entries.slice(0, limit);
|
|
@@ -163,17 +185,39 @@ async function handler(args) {
|
|
|
163
185
|
// Tags (non-system ones)
|
|
164
186
|
if (entry.tags && entry.tags.length > 0) {
|
|
165
187
|
const visibleTags = entry.tags
|
|
166
|
-
.filter(t => !t.startsWith('inspired:') && !t.startsWith('fulfills:') && !t.startsWith('riff:') && !t.startsWith('claim:'))
|
|
188
|
+
.filter(t => !t.startsWith('inspired:') && !t.startsWith('fulfills:') && !t.startsWith('riff:') && !t.startsWith('claim:') && !t.startsWith('observation:') && !t.startsWith('type:'))
|
|
167
189
|
.slice(0, 3);
|
|
168
190
|
if (visibleTags.length > 0) {
|
|
169
191
|
display += ` ${visibleTags.map(t => `#${t}`).join(' ')}\n`;
|
|
170
192
|
}
|
|
171
193
|
}
|
|
172
194
|
|
|
195
|
+
// Observation metadata
|
|
196
|
+
if (entry.category === 'observation' && entry.metadata?.observation_type) {
|
|
197
|
+
display += ` _type: ${entry.metadata.observation_type}_\n`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ASCII art (if present)
|
|
201
|
+
if (entry.metadata?.ascii_art) {
|
|
202
|
+
display += `\n${entry.metadata.ascii_art}\n`;
|
|
203
|
+
display += ` 📊 _includes visualization_\n`;
|
|
204
|
+
}
|
|
205
|
+
|
|
173
206
|
display += ` _${timeAgo}_\n\n`;
|
|
174
207
|
});
|
|
175
208
|
|
|
176
|
-
|
|
209
|
+
// Build response with tip actions if we have ships
|
|
210
|
+
const response = { display };
|
|
211
|
+
|
|
212
|
+
// Find the most recent ship to offer tip action for
|
|
213
|
+
const recentShip = entries.find(e => e.category === 'shipped');
|
|
214
|
+
if (recentShip && recentShip.author.toLowerCase() !== myHandle.toLowerCase()) {
|
|
215
|
+
response.actions = formatActions(actions.afterShipView(recentShip));
|
|
216
|
+
response.hint = 'tip_ship_available';
|
|
217
|
+
response.ship_author = recentShip.author;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return response;
|
|
177
221
|
|
|
178
222
|
} catch (error) {
|
|
179
223
|
return { display: `⚠️ Failed to load feed: ${error.message}` };
|