slashvibe-mcp 0.5.24 → 0.5.25
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 +14 -25
- package/api-auth.js +11 -0
- package/hooks/check-guest-messages.js +5 -1
- package/hooks/check-guest-messages.sh +22 -0
- package/index.js +5 -2
- package/notify.js +4 -1
- package/package.json +4 -3
- package/presence.js +4 -1
- package/tools/call.js +9 -9
- package/tools/guest-session.js +4 -8
- package/tools/inbox.js +4 -1
- package/tools/pair.js +55 -11
- package/tools/start.js +4 -2
- package/version.json +11 -13
package/README.md
CHANGED
|
@@ -116,31 +116,20 @@ This enables visual collaboration (diagrams, mockups, debug screenshots) directl
|
|
|
116
116
|
|
|
117
117
|
---
|
|
118
118
|
|
|
119
|
-
##
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
|
126
|
-
|
|
127
|
-
| `vibe_pair` |
|
|
128
|
-
| `vibe_guest` |
|
|
129
|
-
| `vibe_call` |
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
| API | Status |
|
|
134
|
-
|-----|--------|
|
|
135
|
-
| `/api/pair/request` | Deprecated -- use Matrix room invites |
|
|
136
|
-
| `/api/pair/accept` | Deprecated -- use Matrix room invites |
|
|
137
|
-
| `/api/call/signal` | Deprecated -- use Element/Jitsi |
|
|
138
|
-
| `/api/call/status` | Deprecated -- use Element/Jitsi |
|
|
139
|
-
| `/api/call/ice-servers` | Deprecated -- use Element/Jitsi |
|
|
140
|
-
| `/api/session/guest` | Deprecated -- use Matrix messages |
|
|
141
|
-
| `/api/session/live` | Deprecated -- use Matrix rooms |
|
|
142
|
-
|
|
143
|
-
**Why the change:** Matrix rooms provide persistent history, support N participants (not just pairs), work across models (Claude + Codex + any), and federate with the broader Matrix ecosystem. The pair/guest/call system was limited to 2-player KV-backed sessions with short TTLs.
|
|
119
|
+
## Pair/Guest/Call Transport
|
|
120
|
+
|
|
121
|
+
Pairing, live sessions, guest turns, and WebRTC signaling use the supported
|
|
122
|
+
REST/KV transport. Matrix rooms are an optional collaboration surface, not a
|
|
123
|
+
replacement or migration target for these APIs.
|
|
124
|
+
|
|
125
|
+
| Surface | Contract |
|
|
126
|
+
|---------|----------|
|
|
127
|
+
| `vibe_pair` | Authenticated request plus explicit acceptance |
|
|
128
|
+
| `vibe_guest` | Target opt-in plus accepted pair for human sessions |
|
|
129
|
+
| `vibe_call` | Transcript delivery through the consented guest-session path |
|
|
130
|
+
| `/api/pair`, `/api/pair/request`, `/api/pair/accept` | Supported pairing handshakes |
|
|
131
|
+
| `/api/session/live`, `/api/session/guest` | Supported live collaboration transport |
|
|
132
|
+
| `/api/call/*` | Supported WebRTC signaling and lifecycle transport |
|
|
144
133
|
|
|
145
134
|
---
|
|
146
135
|
|
package/api-auth.js
ADDED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
const fs = require('fs');
|
|
19
19
|
const path = require('path');
|
|
20
|
+
const { apiHeaders } = require('../api-auth');
|
|
20
21
|
|
|
21
22
|
const VIBE_DIR = path.join(process.env.HOME, '.vibe');
|
|
22
23
|
const VIBECODINGS_DIR = path.join(process.env.HOME, '.vibecodings');
|
|
@@ -41,7 +42,10 @@ process.stdin.on('end', () => {});
|
|
|
41
42
|
|
|
42
43
|
// Fetch guest messages (with ack to clear after reading)
|
|
43
44
|
const url = `${API_BASE}/api/session/guest?handle=${encodeURIComponent(handle)}&ack=true`;
|
|
44
|
-
const resp = await fetch(url, {
|
|
45
|
+
const resp = await fetch(url, {
|
|
46
|
+
headers: apiHeaders(),
|
|
47
|
+
signal: AbortSignal.timeout(3000),
|
|
48
|
+
});
|
|
45
49
|
if (!resp.ok) process.exit(0);
|
|
46
50
|
|
|
47
51
|
const data = await resp.json();
|
|
@@ -50,6 +50,23 @@ get_handle() {
|
|
|
50
50
|
echo "$handle"
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
get_token() {
|
|
54
|
+
if ! command -v jq &>/dev/null; then
|
|
55
|
+
return 1
|
|
56
|
+
fi
|
|
57
|
+
local token=""
|
|
58
|
+
if [ -f "$VIBE_DIR/config.json" ]; then
|
|
59
|
+
token=$(jq -r '.authToken // .privyToken // empty' "$VIBE_DIR/config.json" 2>/dev/null)
|
|
60
|
+
fi
|
|
61
|
+
if [ -z "$token" ] && [ -f "$VIBE_DIR/auth.json" ]; then
|
|
62
|
+
token=$(jq -r '.token // empty' "$VIBE_DIR/auth.json" 2>/dev/null)
|
|
63
|
+
fi
|
|
64
|
+
if [ -z "$token" ] && [ -f "$VIBECODINGS_DIR/config.json" ]; then
|
|
65
|
+
token=$(jq -r '.authToken // .privyToken // empty' "$VIBECODINGS_DIR/config.json" 2>/dev/null)
|
|
66
|
+
fi
|
|
67
|
+
echo "$token"
|
|
68
|
+
}
|
|
69
|
+
|
|
53
70
|
# ─── Cooldown check ──────────────────────────────────────────────
|
|
54
71
|
# Prevents hammering the API on rapid successive tool calls
|
|
55
72
|
check_cooldown() {
|
|
@@ -84,6 +101,10 @@ HANDLE=$(get_handle)
|
|
|
84
101
|
if [ -z "$HANDLE" ]; then
|
|
85
102
|
exit 0 # Not authenticated, nothing to check
|
|
86
103
|
fi
|
|
104
|
+
TOKEN=$(get_token || true)
|
|
105
|
+
if [ -z "$TOKEN" ]; then
|
|
106
|
+
exit 0
|
|
107
|
+
fi
|
|
87
108
|
|
|
88
109
|
# Cooldown: skip if we checked recently
|
|
89
110
|
if ! check_cooldown; then
|
|
@@ -95,6 +116,7 @@ update_cooldown
|
|
|
95
116
|
|
|
96
117
|
# Fetch guest messages (with ack=true to clear after reading)
|
|
97
118
|
RESPONSE=$(curl -s --max-time 3 \
|
|
119
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
98
120
|
"${API_BASE}/api/session/guest?handle=$(printf '%s' "$HANDLE" | jq -sRr @uri 2>/dev/null || echo "$HANDLE")&ack=true" \
|
|
99
121
|
2>/dev/null || echo '{"success":false}')
|
|
100
122
|
|
package/index.js
CHANGED
|
@@ -16,6 +16,7 @@ const store = require('./store');
|
|
|
16
16
|
const prompts = require('./prompts');
|
|
17
17
|
const NotificationEmitter = require('./notification-emitter');
|
|
18
18
|
const authStore = require('./auth-store');
|
|
19
|
+
const { apiHeaders } = require('./api-auth');
|
|
19
20
|
|
|
20
21
|
// Tools that shouldn't show presence footer (would be redundant/noisy)
|
|
21
22
|
const SKIP_FOOTER_TOOLS = ['vibe_init', 'vibe_doctor', 'vibe_test', 'vibe_update', 'vibe_settings', 'vibe_notifications'];
|
|
@@ -116,7 +117,9 @@ async function fetchGuestMessages(handle) {
|
|
|
116
117
|
const apiUrl = config.getApiUrl?.() || 'https://www.slashvibe.dev';
|
|
117
118
|
try {
|
|
118
119
|
// Fetch with ack=true to clear after reading (prevents re-injection)
|
|
119
|
-
const resp = await fetch(`${apiUrl}/api/session/guest?handle=${encodeURIComponent(handle)}&ack=true
|
|
120
|
+
const resp = await fetch(`${apiUrl}/api/session/guest?handle=${encodeURIComponent(handle)}&ack=true`, {
|
|
121
|
+
headers: apiHeaders(),
|
|
122
|
+
});
|
|
120
123
|
if (!resp.ok) return [];
|
|
121
124
|
const data = await resp.json();
|
|
122
125
|
if (data.success && Array.isArray(data.messages) && data.messages.length > 0) {
|
|
@@ -626,4 +629,4 @@ class VibeMCPServer {
|
|
|
626
629
|
|
|
627
630
|
// Start
|
|
628
631
|
const server = new VibeMCPServer();
|
|
629
|
-
server.start();
|
|
632
|
+
server.start();
|
package/notify.js
CHANGED
|
@@ -15,6 +15,7 @@ const os = require('os');
|
|
|
15
15
|
const fs = require('fs');
|
|
16
16
|
const path = require('path');
|
|
17
17
|
const config = require('./config');
|
|
18
|
+
const { apiHeaders } = require('./api-auth');
|
|
18
19
|
|
|
19
20
|
// Track what we've already notified about
|
|
20
21
|
const NOTIFY_STATE_FILE = path.join(config.VIBE_DIR, '.notify_state.json');
|
|
@@ -397,7 +398,9 @@ async function checkAll(store) {
|
|
|
397
398
|
// Check for guest session messages (multiplayer)
|
|
398
399
|
try {
|
|
399
400
|
const apiUrl = config.getApiUrl();
|
|
400
|
-
const guestResp = await fetch(`${apiUrl}/api/session/guest?handle=${encodeURIComponent(myHandle)}
|
|
401
|
+
const guestResp = await fetch(`${apiUrl}/api/session/guest?handle=${encodeURIComponent(myHandle)}`, {
|
|
402
|
+
headers: apiHeaders(),
|
|
403
|
+
});
|
|
401
404
|
const guestData = await guestResp.json();
|
|
402
405
|
if (guestData.success && guestData.messages && guestData.messages.length > 0) {
|
|
403
406
|
checkGuestMessages(guestData.messages);
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slashvibe-mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.25",
|
|
4
4
|
"mcpName": "io.github.vibecodinginc/vibe",
|
|
5
|
-
"description": "Social layer for Claude Code -
|
|
5
|
+
"description": "Social layer for Claude Code - identity, presence, messaging, and consented live collaboration",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"slashvibe-mcp": "./cli.js",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"index.js",
|
|
47
47
|
"setup.js",
|
|
48
48
|
"auth-store.js",
|
|
49
|
+
"api-auth.js",
|
|
49
50
|
"auto-update.js",
|
|
50
51
|
"config.js",
|
|
51
52
|
"crypto.js",
|
|
@@ -73,4 +74,4 @@
|
|
|
73
74
|
"crossword-layout-generator": "^0.1.1",
|
|
74
75
|
"node-fetch": "^2.7.0"
|
|
75
76
|
}
|
|
76
|
-
}
|
|
77
|
+
}
|
package/presence.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
const config = require('./config');
|
|
16
16
|
const store = require('./store');
|
|
17
17
|
const notify = require('./notify');
|
|
18
|
+
const { apiHeaders } = require('./api-auth');
|
|
18
19
|
|
|
19
20
|
let heartbeatInterval = null;
|
|
20
21
|
let guestPollInterval = null;
|
|
@@ -99,7 +100,9 @@ async function pollGuestMessages() {
|
|
|
99
100
|
if (!handle) return;
|
|
100
101
|
|
|
101
102
|
const apiUrl = config.getApiUrl?.() || 'https://www.slashvibe.dev';
|
|
102
|
-
const resp = await fetch(`${apiUrl}/api/session/guest?handle=${encodeURIComponent(handle)}
|
|
103
|
+
const resp = await fetch(`${apiUrl}/api/session/guest?handle=${encodeURIComponent(handle)}`, {
|
|
104
|
+
headers: apiHeaders(),
|
|
105
|
+
});
|
|
103
106
|
if (!resp.ok) return;
|
|
104
107
|
|
|
105
108
|
const data = await resp.json();
|
package/tools/call.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* vibe call — Inject call transcripts into paired sessions
|
|
3
3
|
*
|
|
4
|
-
* DEPRECATED: Use Matrix rooms with Jitsi for voice calls. The Jigasi bridge
|
|
5
|
-
* can pipe call transcription (via Whisper/Deepgram) as an ambient API endpoint.
|
|
6
|
-
* Matrix rooms provide persistent transcript history for all participants.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
4
|
* When on a call with your pair partner, paste or pipe in the
|
|
10
5
|
* transcript and it gets sent to both Claude Code sessions.
|
|
11
6
|
*
|
|
@@ -15,12 +10,13 @@
|
|
|
15
10
|
*/
|
|
16
11
|
|
|
17
12
|
const config = require('../config');
|
|
13
|
+
const { apiHeaders } = require('../api-auth');
|
|
18
14
|
|
|
19
15
|
const API_URL = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
|
|
20
16
|
|
|
21
17
|
const definition = {
|
|
22
18
|
name: 'vibe_call',
|
|
23
|
-
description: '
|
|
19
|
+
description: 'Inject call transcripts into an accepted pair partner\'s Claude Code session.',
|
|
24
20
|
inputSchema: {
|
|
25
21
|
type: 'object',
|
|
26
22
|
properties: {
|
|
@@ -67,7 +63,9 @@ async function handler(args) {
|
|
|
67
63
|
let partner = overridePartner;
|
|
68
64
|
if (!partner) {
|
|
69
65
|
try {
|
|
70
|
-
const resp = await fetch(`${API_URL}/api/pair?handle=${encodeURIComponent(handle)}
|
|
66
|
+
const resp = await fetch(`${API_URL}/api/pair?handle=${encodeURIComponent(handle)}`, {
|
|
67
|
+
headers: apiHeaders(),
|
|
68
|
+
});
|
|
71
69
|
const data = await resp.json();
|
|
72
70
|
if (data.paired) {
|
|
73
71
|
partner = data.partner;
|
|
@@ -92,7 +90,9 @@ async function handler(args) {
|
|
|
92
90
|
|
|
93
91
|
async function callStatus(handle) {
|
|
94
92
|
try {
|
|
95
|
-
const resp = await fetch(`${API_URL}/api/pair?handle=${encodeURIComponent(handle)}
|
|
93
|
+
const resp = await fetch(`${API_URL}/api/pair?handle=${encodeURIComponent(handle)}`, {
|
|
94
|
+
headers: apiHeaders(),
|
|
95
|
+
});
|
|
96
96
|
const data = await resp.json();
|
|
97
97
|
|
|
98
98
|
if (!data.paired) {
|
|
@@ -127,7 +127,7 @@ async function injectTranscript(from, to, transcript) {
|
|
|
127
127
|
try {
|
|
128
128
|
const resp = await fetch(`${API_URL}/api/session/guest`, {
|
|
129
129
|
method: 'POST',
|
|
130
|
-
headers: { 'Content-Type': 'application/json' },
|
|
130
|
+
headers: apiHeaders({ 'Content-Type': 'application/json' }),
|
|
131
131
|
body: JSON.stringify({ from, to, message: chunk }),
|
|
132
132
|
});
|
|
133
133
|
const data = await resp.json();
|
package/tools/guest-session.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* vibe guest — Multiplayer session messaging
|
|
3
3
|
*
|
|
4
|
-
* DEPRECATED: Use Matrix rooms instead. The Matrix bot + PostToolUse hook
|
|
5
|
-
* provides the same ambient message injection with persistent history and
|
|
6
|
-
* support for N participants. See vibe_matrix_read, vibe_matrix_send.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
4
|
* Check for guest messages from people watching your session,
|
|
10
5
|
* or send a message into someone else's Claude Code session.
|
|
11
6
|
*
|
|
@@ -14,12 +9,13 @@
|
|
|
14
9
|
*/
|
|
15
10
|
|
|
16
11
|
const config = require('../config');
|
|
12
|
+
const { apiHeaders } = require('../api-auth');
|
|
17
13
|
|
|
18
14
|
const API_URL = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
|
|
19
15
|
|
|
20
16
|
const definition = {
|
|
21
17
|
name: 'vibe_guest',
|
|
22
|
-
description: '
|
|
18
|
+
description: 'Check for consented guest messages in your session, or send a message into an accepted partner\'s Claude Code session.',
|
|
23
19
|
inputSchema: {
|
|
24
20
|
type: 'object',
|
|
25
21
|
properties: {
|
|
@@ -69,7 +65,7 @@ async function handler(args) {
|
|
|
69
65
|
async function checkMessages(handle, ack = false) {
|
|
70
66
|
try {
|
|
71
67
|
const url = `${API_URL}/api/session/guest?handle=${encodeURIComponent(handle)}${ack ? '&ack=true' : ''}`;
|
|
72
|
-
const resp = await fetch(url);
|
|
68
|
+
const resp = await fetch(url, { headers: apiHeaders() });
|
|
73
69
|
const data = await resp.json();
|
|
74
70
|
|
|
75
71
|
if (!data.success || !data.messages || data.messages.length === 0) {
|
|
@@ -100,7 +96,7 @@ async function sendMessage(from, to, message) {
|
|
|
100
96
|
try {
|
|
101
97
|
const resp = await fetch(`${API_URL}/api/session/guest`, {
|
|
102
98
|
method: 'POST',
|
|
103
|
-
headers: { 'Content-Type': 'application/json' },
|
|
99
|
+
headers: apiHeaders({ 'Content-Type': 'application/json' }),
|
|
104
100
|
body: JSON.stringify({ from, to, message }),
|
|
105
101
|
});
|
|
106
102
|
const data = await resp.json();
|
package/tools/inbox.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const config = require('../config');
|
|
6
|
+
const { apiHeaders } = require('../api-auth');
|
|
6
7
|
const store = require('../store');
|
|
7
8
|
const notify = require('../notify');
|
|
8
9
|
const patterns = require('../intelligence/patterns');
|
|
@@ -139,7 +140,9 @@ async function handler(args) {
|
|
|
139
140
|
let guestMessages = [];
|
|
140
141
|
try {
|
|
141
142
|
const apiUrl = config.getApiUrl ? config.getApiUrl() : 'https://www.slashvibe.dev';
|
|
142
|
-
const guestResp = await fetch(`${apiUrl}/api/session/guest?handle=${encodeURIComponent(myHandle)}
|
|
143
|
+
const guestResp = await fetch(`${apiUrl}/api/session/guest?handle=${encodeURIComponent(myHandle)}`, {
|
|
144
|
+
headers: apiHeaders(),
|
|
145
|
+
});
|
|
143
146
|
const guestData = await guestResp.json();
|
|
144
147
|
if (guestData.success && guestData.messages && guestData.messages.length > 0) {
|
|
145
148
|
guestMessages = guestData.messages;
|
package/tools/pair.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* vibe pair — Link up with another user for collaborative coding
|
|
3
3
|
*
|
|
4
|
-
* DEPRECATED: Use Matrix rooms instead. Matrix rooms support N participants,
|
|
5
|
-
* persistent history, and cross-model collaboration (Claude + Codex + any).
|
|
6
|
-
* See vibe_matrix_rooms, vibe_matrix_read, vibe_matrix_send.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
4
|
* When paired:
|
|
10
5
|
* - Your presence shows "paired with @partner"
|
|
11
6
|
* - You can send guest messages into each other's sessions
|
|
@@ -13,19 +8,20 @@
|
|
|
13
8
|
*/
|
|
14
9
|
|
|
15
10
|
const config = require('../config');
|
|
11
|
+
const { apiHeaders } = require('../api-auth');
|
|
16
12
|
|
|
17
13
|
const API_URL = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
|
|
18
14
|
|
|
19
15
|
const definition = {
|
|
20
16
|
name: 'vibe_pair',
|
|
21
|
-
description: '
|
|
17
|
+
description: 'Pair with another /vibe user for collaborative coding. Pairing requires the other actor to accept.',
|
|
22
18
|
inputSchema: {
|
|
23
19
|
type: 'object',
|
|
24
20
|
properties: {
|
|
25
21
|
action: {
|
|
26
22
|
type: 'string',
|
|
27
|
-
description: 'Action: "start"
|
|
28
|
-
enum: ['start', 'stop', 'status'],
|
|
23
|
+
description: 'Action: "start", "accept", "decline", "stop", or "status"',
|
|
24
|
+
enum: ['start', 'accept', 'decline', 'stop', 'status'],
|
|
29
25
|
},
|
|
30
26
|
partner: {
|
|
31
27
|
type: 'string',
|
|
@@ -61,6 +57,11 @@ async function handler(args) {
|
|
|
61
57
|
return await startPair(handle, partner, mode || 'coding');
|
|
62
58
|
}
|
|
63
59
|
|
|
60
|
+
if (action === 'accept' || action === 'decline') {
|
|
61
|
+
if (!partner) return { content: [{ type: 'text', text: `Missing partner for ${action}.` }] };
|
|
62
|
+
return await respondToPair(handle, partner, action);
|
|
63
|
+
}
|
|
64
|
+
|
|
64
65
|
if (action === 'stop') {
|
|
65
66
|
return await stopPair(handle);
|
|
66
67
|
}
|
|
@@ -70,10 +71,22 @@ async function handler(args) {
|
|
|
70
71
|
|
|
71
72
|
async function checkStatus(handle) {
|
|
72
73
|
try {
|
|
73
|
-
const resp = await fetch(`${API_URL}/api/pair?handle=${encodeURIComponent(handle)}
|
|
74
|
+
const resp = await fetch(`${API_URL}/api/pair?handle=${encodeURIComponent(handle)}`, {
|
|
75
|
+
headers: apiHeaders(),
|
|
76
|
+
});
|
|
74
77
|
const data = await resp.json();
|
|
75
78
|
|
|
76
79
|
if (!data.paired) {
|
|
80
|
+
const incoming = data.incomingRequests || [];
|
|
81
|
+
if (incoming.length > 0) {
|
|
82
|
+
const request = incoming[0];
|
|
83
|
+
return {
|
|
84
|
+
content: [{
|
|
85
|
+
type: 'text',
|
|
86
|
+
text: `@${request.from} wants to pair (${request.mode || 'coding'}). Use action "accept" or "decline" with partner "${request.from}".`,
|
|
87
|
+
}],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
77
90
|
return { content: [{ type: 'text', text: 'Not paired with anyone. Use action "start" with a partner handle.' }] };
|
|
78
91
|
}
|
|
79
92
|
|
|
@@ -89,11 +102,33 @@ async function checkStatus(handle) {
|
|
|
89
102
|
}
|
|
90
103
|
}
|
|
91
104
|
|
|
105
|
+
async function respondToPair(handle, partner, action) {
|
|
106
|
+
try {
|
|
107
|
+
const resp = await fetch(`${API_URL}/api/pair`, {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
headers: apiHeaders({ 'Content-Type': 'application/json' }),
|
|
110
|
+
body: JSON.stringify({ handle, partner, action }),
|
|
111
|
+
});
|
|
112
|
+
const data = await resp.json();
|
|
113
|
+
if (!resp.ok || data.success === false) {
|
|
114
|
+
return { content: [{ type: 'text', text: `Could not ${action} pair request: ${data.message || data.error}` }] };
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
content: [{
|
|
118
|
+
type: 'text',
|
|
119
|
+
text: action === 'accept' ? `Paired with @${partner}.` : `Declined @${partner}'s pair request.`,
|
|
120
|
+
}],
|
|
121
|
+
};
|
|
122
|
+
} catch (err) {
|
|
123
|
+
return { content: [{ type: 'text', text: `Error: ${err.message}` }] };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
92
127
|
async function startPair(handle, partner, mode) {
|
|
93
128
|
try {
|
|
94
129
|
const resp = await fetch(`${API_URL}/api/pair`, {
|
|
95
130
|
method: 'POST',
|
|
96
|
-
headers: { 'Content-Type': 'application/json' },
|
|
131
|
+
headers: apiHeaders({ 'Content-Type': 'application/json' }),
|
|
97
132
|
body: JSON.stringify({ handle, partner, action: 'start', mode }),
|
|
98
133
|
});
|
|
99
134
|
const data = await resp.json();
|
|
@@ -107,6 +142,15 @@ async function startPair(handle, partner, mode) {
|
|
|
107
142
|
};
|
|
108
143
|
}
|
|
109
144
|
|
|
145
|
+
if (data.pending) {
|
|
146
|
+
return {
|
|
147
|
+
content: [{
|
|
148
|
+
type: 'text',
|
|
149
|
+
text: `Pair request sent to @${partner}. Session sharing starts only after they accept.`,
|
|
150
|
+
}],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
110
154
|
return { content: [{ type: 'text', text: `Failed to pair: ${JSON.stringify(data)}` }] };
|
|
111
155
|
} catch (err) {
|
|
112
156
|
return { content: [{ type: 'text', text: `Error: ${err.message}` }] };
|
|
@@ -117,7 +161,7 @@ async function stopPair(handle) {
|
|
|
117
161
|
try {
|
|
118
162
|
await fetch(`${API_URL}/api/pair`, {
|
|
119
163
|
method: 'POST',
|
|
120
|
-
headers: { 'Content-Type': 'application/json' },
|
|
164
|
+
headers: apiHeaders({ 'Content-Type': 'application/json' }),
|
|
121
165
|
body: JSON.stringify({ handle, action: 'stop' }),
|
|
122
166
|
});
|
|
123
167
|
return { content: [{ type: 'text', text: 'Pair ended.' }] };
|
package/tools/start.js
CHANGED
|
@@ -12,6 +12,7 @@ const fs = require('fs');
|
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const { execSync } = require('child_process');
|
|
14
14
|
const config = require('../config');
|
|
15
|
+
const { apiHeaders } = require('../api-auth');
|
|
15
16
|
const store = require('../store');
|
|
16
17
|
const memory = require('../memory');
|
|
17
18
|
const notify = require('../notify');
|
|
@@ -274,9 +275,10 @@ async function handler(args) {
|
|
|
274
275
|
let pairStatus = null;
|
|
275
276
|
try {
|
|
276
277
|
const apiUrl = config.getApiUrl();
|
|
278
|
+
const headers = apiHeaders();
|
|
277
279
|
const [guestResp, pairResp] = await Promise.all([
|
|
278
|
-
fetch(`${apiUrl}/api/session/guest?handle=${encodeURIComponent(myHandle)}
|
|
279
|
-
fetch(`${apiUrl}/api/pair?handle=${encodeURIComponent(myHandle)}
|
|
280
|
+
fetch(`${apiUrl}/api/session/guest?handle=${encodeURIComponent(myHandle)}`, { headers }),
|
|
281
|
+
fetch(`${apiUrl}/api/pair?handle=${encodeURIComponent(myHandle)}`, { headers }),
|
|
280
282
|
]);
|
|
281
283
|
const guestData = await guestResp.json();
|
|
282
284
|
if (guestData.success && guestData.messages && guestData.messages.length > 0) {
|
package/version.json
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.5.
|
|
3
|
-
"updated": "2026-07-
|
|
4
|
-
"changelog": "
|
|
2
|
+
"version": "0.5.25",
|
|
3
|
+
"updated": "2026-07-10",
|
|
4
|
+
"changelog": "Live collaboration now has a real actor boundary. Pairing requires acceptance, private session reads use the signed-in actor instead of a viewer string, and messages enter a human session only after the owner opts in. All MCP pair and guest calls now carry the OAuth token.",
|
|
5
5
|
"features": [
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
6
|
+
"Pair requests are two-party: the target accepts or declines before session context is shared",
|
|
7
|
+
"Paired and private session reads derive the viewer from a signed token; query-string handle spoofing no longer grants access",
|
|
8
|
+
"Guest messages and handle/claude DMs require target opt-in plus an accepted pair for human sessions",
|
|
9
|
+
"Public house-agent sessions retain their explicit open-door policy",
|
|
10
|
+
"The MCP version manifest, package, and lockfile now report the same release"
|
|
9
11
|
],
|
|
10
|
-
"deprecated": [
|
|
11
|
-
|
|
12
|
-
"vibe_guest \u2014 use vibe_matrix_send / vibe_matrix_read",
|
|
13
|
-
"vibe_call \u2014 use Matrix rooms + Jitsi"
|
|
14
|
-
],
|
|
15
|
-
"breaking": false,
|
|
12
|
+
"deprecated": [],
|
|
13
|
+
"breaking": true,
|
|
16
14
|
"updateUrl": "https://www.slashvibe.dev/api/version"
|
|
17
|
-
}
|
|
15
|
+
}
|