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/wallet.js
CHANGED
|
@@ -1,216 +1,269 @@
|
|
|
1
|
-
const { ensureWallet, getWalletAddress, getBalance, hasWallet } = require('../../lib/cdp/wallet-helpers');
|
|
2
|
-
const { sql } = require('../../api/lib/db');
|
|
3
|
-
|
|
4
1
|
/**
|
|
5
|
-
*
|
|
2
|
+
* vibe_wallet - Unified wallet dashboard
|
|
3
|
+
*
|
|
4
|
+
* The "smoothbrain" view - everything in one place:
|
|
5
|
+
* - USD earnings (fiat)
|
|
6
|
+
* - $VIBE tokens (engagement currency)
|
|
7
|
+
* - Vesting schedule
|
|
8
|
+
* - Quick actions
|
|
6
9
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* vibe wallet withdraw # Withdraw to Coinbase/bank
|
|
12
|
-
* vibe wallet history # Transaction history
|
|
10
|
+
* Examples:
|
|
11
|
+
* - "vibe wallet"
|
|
12
|
+
* - "check my balance"
|
|
13
|
+
* - "show my $VIBE"
|
|
13
14
|
*/
|
|
14
15
|
|
|
16
|
+
const fetch = require('node-fetch');
|
|
17
|
+
|
|
15
18
|
module.exports = {
|
|
16
19
|
name: 'vibe_wallet',
|
|
17
|
-
description: '
|
|
18
|
-
|
|
20
|
+
description: 'Check your wallet: USD earnings, $VIBE tokens, vesting schedule, and available actions.',
|
|
21
|
+
|
|
22
|
+
inputSchema: {
|
|
19
23
|
type: 'object',
|
|
20
24
|
properties: {
|
|
21
|
-
|
|
25
|
+
view: {
|
|
22
26
|
type: 'string',
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
description: 'Which view: dashboard (default), vesting, history',
|
|
28
|
+
enum: ['dashboard', 'vesting', 'history'],
|
|
29
|
+
default: 'dashboard'
|
|
25
30
|
},
|
|
26
|
-
|
|
31
|
+
limit: {
|
|
27
32
|
type: 'number',
|
|
28
|
-
description: '
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
type: 'string',
|
|
32
|
-
description: 'Destination address for withdrawal',
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
handler: async (args, context) => {
|
|
38
|
-
const { action = 'status' } = args;
|
|
39
|
-
const handle = context.handle; // From authenticated session
|
|
40
|
-
|
|
41
|
-
if (!handle) {
|
|
42
|
-
return {
|
|
43
|
-
error: true,
|
|
44
|
-
message: 'Not authenticated. Run: vibe init @yourhandle',
|
|
45
|
-
};
|
|
33
|
+
description: 'Number of transactions/items to show (for history view)',
|
|
34
|
+
default: 10
|
|
35
|
+
}
|
|
46
36
|
}
|
|
37
|
+
},
|
|
47
38
|
|
|
39
|
+
async execute({ view = 'dashboard', limit = 10 }, context) {
|
|
48
40
|
try {
|
|
49
|
-
|
|
50
|
-
if (action === 'create') {
|
|
51
|
-
const exists = await hasWallet(handle);
|
|
52
|
-
|
|
53
|
-
if (exists) {
|
|
54
|
-
const address = await getWalletAddress(handle);
|
|
55
|
-
const balance = await getBalance(handle);
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
success: false,
|
|
59
|
-
message: `You already have a wallet!
|
|
60
|
-
|
|
61
|
-
Address: ${address}
|
|
62
|
-
Balance: $${balance.toFixed(2)} USDC
|
|
63
|
-
Network: Base
|
|
64
|
-
|
|
65
|
-
Commands:
|
|
66
|
-
vibe wallet # View status
|
|
67
|
-
vibe wallet deposit # Add funds
|
|
68
|
-
`,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Create wallet
|
|
73
|
-
const address = await ensureWallet(handle, 'explicit_create');
|
|
41
|
+
const handle = context.handle;
|
|
74
42
|
|
|
43
|
+
if (!handle) {
|
|
75
44
|
return {
|
|
76
|
-
success:
|
|
77
|
-
|
|
78
|
-
message: `✓ Wallet created on Base!
|
|
79
|
-
|
|
80
|
-
Address: ${address}
|
|
81
|
-
Balance: $0.00 USDC
|
|
82
|
-
Network: Base (Coinbase L2)
|
|
83
|
-
|
|
84
|
-
Next steps:
|
|
85
|
-
vibe wallet deposit # Add USDC to start transacting
|
|
86
|
-
vibe wallet # Check balance anytime
|
|
87
|
-
|
|
88
|
-
Your wallet is a smart contract - no gas fees, no seed phrases.
|
|
89
|
-
Managed by Coinbase Developer Platform.
|
|
90
|
-
`,
|
|
45
|
+
success: false,
|
|
46
|
+
error: 'Not authenticated. Use vibe init first.'
|
|
91
47
|
};
|
|
92
48
|
}
|
|
93
49
|
|
|
94
|
-
|
|
95
|
-
if (action === 'deposit') {
|
|
96
|
-
const address = await getWalletAddress(handle);
|
|
97
|
-
|
|
98
|
-
if (!address) {
|
|
99
|
-
return {
|
|
100
|
-
error: true,
|
|
101
|
-
message: `No wallet yet!
|
|
102
|
-
|
|
103
|
-
You'll get a wallet automatically when:
|
|
104
|
-
• Someone pays you
|
|
105
|
-
• You pay for a service
|
|
50
|
+
const apiUrl = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
|
|
106
51
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return {
|
|
113
|
-
success: true,
|
|
114
|
-
address,
|
|
115
|
-
message: `💳 Deposit USDC to your /vibe wallet
|
|
116
|
-
|
|
117
|
-
Address: ${address}
|
|
118
|
-
Network: Base (Coinbase L2)
|
|
119
|
-
Token: USDC
|
|
120
|
-
|
|
121
|
-
Options:
|
|
122
|
-
1. Coinbase → Withdraw to this address (instant)
|
|
123
|
-
2. Bridge from Ethereum: https://bridge.base.org
|
|
124
|
-
3. Send USDC directly on Base network
|
|
125
|
-
|
|
126
|
-
⚠️ Important:
|
|
127
|
-
- Only send USDC on Base network
|
|
128
|
-
- Do NOT send ETH or other tokens (will be lost)
|
|
129
|
-
- Minimum: $1 USDC
|
|
130
|
-
|
|
131
|
-
Check balance: vibe wallet
|
|
132
|
-
`,
|
|
133
|
-
};
|
|
52
|
+
// Handle different views
|
|
53
|
+
if (view === 'vesting') {
|
|
54
|
+
return await fetchVestingSchedule(apiUrl, handle, context.token);
|
|
134
55
|
}
|
|
135
56
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (!address) {
|
|
140
|
-
return {
|
|
141
|
-
success: false,
|
|
142
|
-
message: `No wallet yet!
|
|
143
|
-
|
|
144
|
-
You'll get a wallet automatically when:
|
|
145
|
-
• Someone pays you for helping
|
|
146
|
-
• You pay for a service (like ping.money expert help)
|
|
147
|
-
• You receive funds from another user
|
|
148
|
-
|
|
149
|
-
This keeps /vibe simple - wallets emerge only when you transact.
|
|
150
|
-
|
|
151
|
-
Want to create one now?
|
|
152
|
-
vibe wallet create
|
|
153
|
-
|
|
154
|
-
Questions?
|
|
155
|
-
Wallets are smart contracts on Base (Coinbase's L2)
|
|
156
|
-
No gas fees, no seed phrases, managed by CDP
|
|
157
|
-
`,
|
|
158
|
-
};
|
|
57
|
+
if (view === 'history') {
|
|
58
|
+
return await fetchHistory(apiUrl, handle, limit, context.token);
|
|
159
59
|
}
|
|
160
60
|
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
// Get recent transactions
|
|
165
|
-
const cleanHandle = handle.replace('@', '');
|
|
166
|
-
const txResult = await sql`
|
|
167
|
-
SELECT event_type, amount, metadata, created_at
|
|
168
|
-
FROM wallet_events
|
|
169
|
-
WHERE handle = ${cleanHandle}
|
|
170
|
-
ORDER BY created_at DESC
|
|
171
|
-
LIMIT 5
|
|
172
|
-
`;
|
|
173
|
-
|
|
174
|
-
const recentTx = txResult.map(tx => {
|
|
175
|
-
const amount = tx.amount ? `$${parseFloat(tx.amount).toFixed(2)}` : '';
|
|
176
|
-
const type = tx.event_type;
|
|
177
|
-
const date = new Date(tx.created_at).toLocaleDateString();
|
|
178
|
-
return ` ${type.padEnd(12)} ${amount.padEnd(8)} ${date}`;
|
|
179
|
-
});
|
|
61
|
+
// Default: dashboard view
|
|
62
|
+
return await fetchDashboard(apiUrl, handle, context.token);
|
|
180
63
|
|
|
181
|
-
return {
|
|
182
|
-
success: true,
|
|
183
|
-
address,
|
|
184
|
-
balance,
|
|
185
|
-
message: `💰 Your /vibe Wallet
|
|
186
|
-
|
|
187
|
-
Address: ${address.slice(0, 8)}...${address.slice(-6)}
|
|
188
|
-
Balance: $${balance.toFixed(2)} USDC
|
|
189
|
-
Network: Base (Coinbase L2)
|
|
190
|
-
|
|
191
|
-
Recent activity:
|
|
192
|
-
${recentTx.length > 0 ? recentTx.join('\n') : ' No transactions yet'}
|
|
193
|
-
|
|
194
|
-
Commands:
|
|
195
|
-
vibe wallet deposit # Add funds
|
|
196
|
-
vibe wallet withdraw # Send to Coinbase/bank
|
|
197
|
-
vibe wallet history # Full transaction log
|
|
198
|
-
|
|
199
|
-
View on Basescan:
|
|
200
|
-
https://basescan.org/address/${address}
|
|
201
|
-
`,
|
|
202
|
-
};
|
|
203
64
|
} catch (error) {
|
|
204
|
-
console.error('Wallet error:', error);
|
|
205
|
-
|
|
206
65
|
return {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
If this persists, contact support or check:
|
|
211
|
-
https://docs.cdp.coinbase.com/
|
|
212
|
-
`,
|
|
66
|
+
success: false,
|
|
67
|
+
error: 'Failed to fetch wallet',
|
|
68
|
+
details: error.message
|
|
213
69
|
};
|
|
214
70
|
}
|
|
215
|
-
}
|
|
71
|
+
}
|
|
216
72
|
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Fetch unified dashboard
|
|
76
|
+
*/
|
|
77
|
+
async function fetchDashboard(apiUrl, handle, token) {
|
|
78
|
+
const response = await fetch(
|
|
79
|
+
`${apiUrl}/api/wallet/dashboard?handle=${handle}`,
|
|
80
|
+
{ headers: { 'Authorization': `Bearer ${token}` } }
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const result = await response.json();
|
|
84
|
+
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
return {
|
|
87
|
+
success: false,
|
|
88
|
+
error: 'Failed to fetch wallet dashboard',
|
|
89
|
+
details: result.error
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Build formatted output
|
|
94
|
+
const usd = result.usd;
|
|
95
|
+
const vibe = result.vibe;
|
|
96
|
+
const summary = result.summary;
|
|
97
|
+
const actions = result.actions || [];
|
|
98
|
+
|
|
99
|
+
// USD section
|
|
100
|
+
const usdSection = usd.available.cents > 0
|
|
101
|
+
? `💵 USD Earnings
|
|
102
|
+
Available: ${usd.available.display}
|
|
103
|
+
${usd.held.cents > 0 ? `Held: ${usd.held.display}\n ` : ''}Total earned: ${usd.total_earned.display}
|
|
104
|
+
${usd.can_withdraw ? '✅ Ready to withdraw' : usd.payout_enabled ? '⏳ No balance to withdraw' : '⚠️ Connect bank to withdraw'}`
|
|
105
|
+
: `💵 USD Earnings
|
|
106
|
+
No earnings yet. Complete gigs to earn!`;
|
|
107
|
+
|
|
108
|
+
// $VIBE section
|
|
109
|
+
const vibeSection = `
|
|
110
|
+
💜 $VIBE Tokens
|
|
111
|
+
Spendable: ${vibe.liquid} $VIBE
|
|
112
|
+
Vesting: ${vibe.vesting} $VIBE
|
|
113
|
+
─────────────────
|
|
114
|
+
Total: ${vibe.total} $VIBE
|
|
115
|
+
|
|
116
|
+
${vibe.upcoming.this_week > 0 ? `📅 +${vibe.upcoming.this_week} unlocks this week` : ''}
|
|
117
|
+
${vibe.upcoming.this_month > 0 && vibe.upcoming.this_month !== vibe.upcoming.this_week ? `📅 +${vibe.upcoming.this_month} unlocks this month` : ''}
|
|
118
|
+
${vibe.next_unlock ? `⏰ Next: ${vibe.next_unlock.amount} $VIBE in ${vibe.next_unlock.days} days` : ''}
|
|
119
|
+
|
|
120
|
+
Daily: ${vibe.daily.earned}/${vibe.daily.cap} earned today
|
|
121
|
+
${'█'.repeat(Math.floor(vibe.daily.percent / 10))}${'░'.repeat(10 - Math.floor(vibe.daily.percent / 10))} ${vibe.daily.percent}%`;
|
|
122
|
+
|
|
123
|
+
// Actions section
|
|
124
|
+
const actionsSection = actions.length > 0
|
|
125
|
+
? `\n🎯 Actions\n${actions.slice(0, 3).map(a => ` • ${a.label}: ${a.description}`).join('\n')}`
|
|
126
|
+
: '';
|
|
127
|
+
|
|
128
|
+
const formatted = `
|
|
129
|
+
╭───────────────────────────────────╮
|
|
130
|
+
│ 💰 WALLET: @${handle.padEnd(17)}│
|
|
131
|
+
╰───────────────────────────────────╯
|
|
132
|
+
|
|
133
|
+
${usdSection}
|
|
134
|
+
|
|
135
|
+
${vibeSection.trim()}
|
|
136
|
+
${actionsSection}
|
|
137
|
+
|
|
138
|
+
Tip: Use "vibe wallet --view vesting" for full schedule
|
|
139
|
+
`.trim();
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
success: true,
|
|
143
|
+
handle,
|
|
144
|
+
usd: result.usd,
|
|
145
|
+
vibe: result.vibe,
|
|
146
|
+
actions: result.actions,
|
|
147
|
+
formatted
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Fetch vesting schedule
|
|
153
|
+
*/
|
|
154
|
+
async function fetchVestingSchedule(apiUrl, handle, token) {
|
|
155
|
+
const response = await fetch(
|
|
156
|
+
`${apiUrl}/api/vibe/vesting?handle=${handle}`,
|
|
157
|
+
{ headers: { 'Authorization': `Bearer ${token}` } }
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const result = await response.json();
|
|
161
|
+
|
|
162
|
+
if (!response.ok) {
|
|
163
|
+
return {
|
|
164
|
+
success: false,
|
|
165
|
+
error: 'Failed to fetch vesting schedule',
|
|
166
|
+
details: result.error
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Format vesting schedule
|
|
171
|
+
const summary = result.summary;
|
|
172
|
+
const unlocks = result.unlock_schedule || [];
|
|
173
|
+
const breakdown = result.activity_breakdown || [];
|
|
174
|
+
|
|
175
|
+
const scheduleLines = unlocks.slice(0, 8).map(week => {
|
|
176
|
+
const bar = '█'.repeat(Math.min(10, Math.ceil(week.total_amount / 10)));
|
|
177
|
+
return ` ${week.week_label.padEnd(12)} +${week.total_amount} $VIBE ${bar}`;
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const breakdownLines = breakdown.slice(0, 5).map(act => {
|
|
181
|
+
return ` ${act.reason_display.padEnd(24)} ${act.amount} $VIBE`;
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const formatted = `
|
|
185
|
+
╭───────────────────────────────────╮
|
|
186
|
+
│ 📅 VESTING SCHEDULE: @${handle.padEnd(10)}│
|
|
187
|
+
╰───────────────────────────────────╯
|
|
188
|
+
|
|
189
|
+
Summary
|
|
190
|
+
Liquid (spendable): ${summary.liquid} $VIBE
|
|
191
|
+
Vesting (locked): ${summary.vesting} $VIBE
|
|
192
|
+
─────────────────────────────
|
|
193
|
+
Total: ${summary.total} $VIBE
|
|
194
|
+
|
|
195
|
+
${result.next_unlock ? `Next Unlock
|
|
196
|
+
${result.next_unlock.amount} $VIBE in ${result.next_unlock.days_remaining} days
|
|
197
|
+
(earned from: ${result.next_unlock.reason})
|
|
198
|
+
` : ''}
|
|
199
|
+
Upcoming Unlocks by Week
|
|
200
|
+
${scheduleLines.length > 0 ? scheduleLines.join('\n') : ' No tokens vesting'}
|
|
201
|
+
|
|
202
|
+
Earned By Activity
|
|
203
|
+
${breakdownLines.length > 0 ? breakdownLines.join('\n') : ' No activity yet'}
|
|
204
|
+
|
|
205
|
+
ℹ️ Tokens vest over ${result.vesting_period_days} days to reward long-term builders.
|
|
206
|
+
`.trim();
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
success: true,
|
|
210
|
+
handle,
|
|
211
|
+
summary: result.summary,
|
|
212
|
+
schedule: result.unlock_schedule,
|
|
213
|
+
breakdown: result.activity_breakdown,
|
|
214
|
+
formatted
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Fetch transaction history
|
|
220
|
+
*/
|
|
221
|
+
async function fetchHistory(apiUrl, handle, limit, token) {
|
|
222
|
+
const response = await fetch(
|
|
223
|
+
`${apiUrl}/api/vibe/history?handle=${handle}&limit=${limit}`,
|
|
224
|
+
{ headers: { 'Authorization': `Bearer ${token}` } }
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
const result = await response.json();
|
|
228
|
+
|
|
229
|
+
if (!response.ok) {
|
|
230
|
+
return {
|
|
231
|
+
success: false,
|
|
232
|
+
error: 'Failed to fetch history',
|
|
233
|
+
details: result.error
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const transactions = result.transactions || [];
|
|
238
|
+
|
|
239
|
+
const txLines = transactions.map(tx => {
|
|
240
|
+
const icon = tx.amount > 0 ? '↓' : '↑';
|
|
241
|
+
const sign = tx.amount > 0 ? '+' : '';
|
|
242
|
+
const status = tx.status === 'vesting' ? '🔒' : '✓';
|
|
243
|
+
const date = new Date(tx.created_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
244
|
+
return ` ${icon} ${sign}${tx.amount} $VIBE ${tx.reason.padEnd(20)} ${status} ${date}`;
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const formatted = `
|
|
248
|
+
╭───────────────────────────────────╮
|
|
249
|
+
│ 📜 HISTORY: @${handle.padEnd(17)}│
|
|
250
|
+
╰───────────────────────────────────╯
|
|
251
|
+
|
|
252
|
+
Balance: ${result.balance.liquid} liquid / ${result.balance.vesting} vesting
|
|
253
|
+
|
|
254
|
+
Recent Transactions
|
|
255
|
+
${txLines.length > 0 ? txLines.join('\n') : ' No transactions yet'}
|
|
256
|
+
|
|
257
|
+
${result.pagination.has_more ? `\n(showing ${limit} of more...)` : ''}
|
|
258
|
+
|
|
259
|
+
Legend: ↓ earned ↑ spent 🔒 vesting ✓ liquid
|
|
260
|
+
`.trim();
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
success: true,
|
|
264
|
+
handle,
|
|
265
|
+
balance: result.balance,
|
|
266
|
+
transactions: result.transactions,
|
|
267
|
+
formatted
|
|
268
|
+
};
|
|
269
|
+
}
|
package/tools/watch.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe watch — Watch live broadcasts
|
|
3
|
+
*
|
|
4
|
+
* Discover and watch live coding sessions from the /vibe community.
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* - watch → List active broadcasts
|
|
8
|
+
* - watch @handle → Watch specific person's broadcast
|
|
9
|
+
* - watch room_xxx → Watch by room ID
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const config = require('../config');
|
|
13
|
+
|
|
14
|
+
const definition = {
|
|
15
|
+
name: 'vibe_watch',
|
|
16
|
+
description: 'See who\'s live and watch their coding sessions in real-time.',
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
target: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'Handle (@user) or room ID to watch. Omit to list all live broadcasts.'
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
async function handler(args) {
|
|
29
|
+
const apiUrl = config.getApiUrl();
|
|
30
|
+
const target = args.target;
|
|
31
|
+
|
|
32
|
+
// LIST all broadcasts
|
|
33
|
+
if (!target) {
|
|
34
|
+
try {
|
|
35
|
+
const response = await fetch(`${apiUrl}/api/live`, {
|
|
36
|
+
headers: { 'Accept': 'application/json' }
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const data = await response.json();
|
|
40
|
+
|
|
41
|
+
if (!data.success) {
|
|
42
|
+
return { display: `⚠️ Failed to fetch live broadcasts: ${data.error}` };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!data.broadcasts || data.broadcasts.length === 0) {
|
|
46
|
+
let display = `📺 **No Live Broadcasts**\n\n`;
|
|
47
|
+
display += `_No one is streaming right now._\n\n`;
|
|
48
|
+
display += `**Start your own:**\n`;
|
|
49
|
+
display += `\`vibe broadcast start "What you're building"\`\n\n`;
|
|
50
|
+
display += `**Browse saved sessions:**\n`;
|
|
51
|
+
display += `\`vibe session list\``;
|
|
52
|
+
return { display };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const formatDuration = (secs) => {
|
|
56
|
+
if (secs < 60) return 'just started';
|
|
57
|
+
if (secs < 3600) return `${Math.floor(secs / 60)}m`;
|
|
58
|
+
return `${Math.floor(secs / 3600)}h ${Math.floor((secs % 3600) / 60)}m`;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
let display = `📺 **${data.broadcasts.length} Live Now**\n\n`;
|
|
62
|
+
|
|
63
|
+
for (const b of data.broadcasts) {
|
|
64
|
+
const viewers = b.viewers || 0;
|
|
65
|
+
const duration = formatDuration(b.duration || 0);
|
|
66
|
+
|
|
67
|
+
display += `🔴 **@${b.handle}**`;
|
|
68
|
+
if (viewers > 0) {
|
|
69
|
+
display += ` — ${viewers} watching`;
|
|
70
|
+
}
|
|
71
|
+
display += ` — ${duration}\n`;
|
|
72
|
+
display += ` → \`vibe watch ${b.roomId}\`\n\n`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
display += `_Watch a stream or start your own with \`vibe broadcast start\`_`;
|
|
76
|
+
|
|
77
|
+
return { display };
|
|
78
|
+
|
|
79
|
+
} catch (error) {
|
|
80
|
+
return { display: `## Watch Error\n\n${error.message}` };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// WATCH specific broadcast
|
|
85
|
+
const isRoomId = target.startsWith('room_');
|
|
86
|
+
const isHandle = target.startsWith('@') || !target.includes('_');
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
let roomId = target;
|
|
90
|
+
let broadcasterHandle = null;
|
|
91
|
+
|
|
92
|
+
// If target is a handle, find their room
|
|
93
|
+
if (isHandle) {
|
|
94
|
+
broadcasterHandle = target.replace('@', '').toLowerCase();
|
|
95
|
+
|
|
96
|
+
// Fetch live broadcasts to find this person's room
|
|
97
|
+
const liveResponse = await fetch(`${apiUrl}/api/live`, {
|
|
98
|
+
headers: { 'Accept': 'application/json' }
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const liveData = await liveResponse.json();
|
|
102
|
+
|
|
103
|
+
if (!liveData.success || !liveData.broadcasts) {
|
|
104
|
+
return { display: `⚠️ Failed to fetch broadcasts` };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const broadcast = liveData.broadcasts.find(
|
|
108
|
+
b => b.handle.toLowerCase() === broadcasterHandle
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
if (!broadcast) {
|
|
112
|
+
return {
|
|
113
|
+
display: `📺 @${broadcasterHandle} is not live right now.\n\n_Check who's streaming with \`vibe watch\`_`
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
roomId = broadcast.roomId;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Get broadcast info
|
|
121
|
+
const response = await fetch(`${apiUrl}/api/watch?room=${roomId}`);
|
|
122
|
+
const data = await response.json();
|
|
123
|
+
|
|
124
|
+
if (!data.success) {
|
|
125
|
+
return {
|
|
126
|
+
display: `📺 Broadcast not found or ended.\n\n_Check live streams with \`vibe watch\`_`
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const broadcast = data.broadcast;
|
|
131
|
+
const duration = broadcast.startedAt
|
|
132
|
+
? Math.round((Date.now() - new Date(broadcast.startedAt).getTime()) / 1000)
|
|
133
|
+
: 0;
|
|
134
|
+
|
|
135
|
+
const formatDuration = (secs) => {
|
|
136
|
+
if (secs < 60) return 'just started';
|
|
137
|
+
if (secs < 3600) return `${Math.floor(secs / 60)}m`;
|
|
138
|
+
return `${Math.floor(secs / 3600)}h ${Math.floor((secs % 3600) / 60)}m`;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
let display = `🔴 **LIVE: @${broadcast.handle}**\n\n`;
|
|
142
|
+
display += `**Duration:** ${formatDuration(duration)}\n`;
|
|
143
|
+
display += `**Viewers:** ${broadcast.viewerCount || 0}\n\n`;
|
|
144
|
+
display += `**Watch in browser:**\n`;
|
|
145
|
+
display += `https://slashvibe.dev/watch/${roomId}\n\n`;
|
|
146
|
+
display += `_Open the URL above to watch the live terminal stream._\n\n`;
|
|
147
|
+
display += `---\n`;
|
|
148
|
+
display += `_Coming soon: inline terminal replay in Claude Code_`;
|
|
149
|
+
|
|
150
|
+
return { display };
|
|
151
|
+
|
|
152
|
+
} catch (error) {
|
|
153
|
+
return { display: `## Watch Error\n\n${error.message}` };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
module.exports = { definition, handler };
|