diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index c892249..03c2808 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -244,6 +244,9 @@ onMounted(async () => {
// Connect to WebSocket for Claude status updates
connectStatusWs()
+ // Fire torch connection early (don't await yet)
+ const torchReady = initTorch()
+
// Initialize WebMCP connection
await initWebMCP()
@@ -320,8 +323,8 @@ onMounted(async () => {
})
}
- // Initialize torch system (handles MCP connection based on torch state)
- await initTorch()
+ // Ensure torch connection is established
+ await torchReady
})
onUnmounted(() => {
diff --git a/frontend/src/components/TorchButton.vue b/frontend/src/components/TorchButton.vue
index 3055e0e..5d41d56 100644
--- a/frontend/src/components/TorchButton.vue
+++ b/frontend/src/components/TorchButton.vue
@@ -1,23 +1,26 @@
-
+
+
+
+
+
+
+
diff --git a/frontend/src/services/toolRegistry.ts b/frontend/src/services/toolRegistry.ts
index 5e707c8..dcec961 100644
--- a/frontend/src/services/toolRegistry.ts
+++ b/frontend/src/services/toolRegistry.ts
@@ -170,6 +170,7 @@ const pageCategories: Record = {
let currentPage: PageName | null = null
let isInitialized = false
+let pendingTorchConnection = false
/**
* Check if connected to MCP
@@ -185,6 +186,12 @@ function isConnectedToMCP(): boolean {
export function initToolRegistry(router: any) {
setRouter(router)
isInitialized = true
+
+ // Fulfill any pending torch connection that arrived before init
+ if (pendingTorchConnection) {
+ pendingTorchConnection = false
+ onTorchConnected()
+ }
}
/**
@@ -291,7 +298,8 @@ export async function initToolsOnRefresh(pageName: PageName) {
*/
export async function onTorchConnected() {
if (!isInitialized) {
- console.warn('[ToolRegistry] Not initialized')
+ console.log('[ToolRegistry] Not initialized yet, deferring torch connection')
+ pendingTorchConnection = true
return
}
diff --git a/frontend/src/services/tools/handlers/torchHandlers.ts b/frontend/src/services/tools/handlers/torchHandlers.ts
index 8d57142..2b7600a 100644
--- a/frontend/src/services/tools/handlers/torchHandlers.ts
+++ b/frontend/src/services/tools/handlers/torchHandlers.ts
@@ -25,7 +25,7 @@ export function createTorchHandlers(): ToolConfig[] {
const isMe = client.id === status.clientId
const hasTorch = client.hasTorch
- result += `${hasTorch ? '🔥' : '⚪'} ${client.id}${isMe ? ' (este browser)' : ''}\n`
+ result += `${hasTorch ? '🔥' : '⚪'} ${client.name || 'Anonymous'} (${client.id})${isMe ? ' (este browser)' : ''}\n`
result += ` Hostname: ${client.hostname}\n`
result += ` User Agent: ${client.userAgent.substring(0, 80)}...\n`
result += ` Conectado: ${client.connectedAt}\n\n`
@@ -46,8 +46,10 @@ export function createTorchHandlers(): ToolConfig[] {
const status = getTorchStatus()
const clients = getTorchClients()
+ const myClient = clients.find(c => c.id === status.clientId)
let result = '=== Estado de la Antorcha ===\n\n'
result += `Mi ID: ${status.clientId || 'No registrado'}\n`
+ result += `Mi nombre: ${myClient?.name || 'Anonymous'}\n`
result += `Tengo la antorcha: ${status.hasTorch ? 'Si' : 'No'}\n`
result += `Holder actual: ${status.torchHolderId || 'Nadie'}\n`
result += `Clientes conectados: ${status.clientCount}\n\n`
@@ -56,6 +58,7 @@ export function createTorchHandlers(): ToolConfig[] {
const holder = clients.find(c => c.id === status.torchHolderId)
if (holder) {
result += `Detalles del holder:\n`
+ result += ` Nombre: ${holder.name || 'Anonymous'}\n`
result += ` Hostname: ${holder.hostname}\n`
result += ` Conectado desde: ${holder.connectedAt}\n`
}
@@ -89,16 +92,17 @@ export function createTorchHandlers(): ToolConfig[] {
const targetExists = clients.some(c => c.id === args.target_id)
if (!targetExists) {
- return `Error: Cliente "${args.target_id}" no encontrado.\n\nClientes disponibles:\n${clients.map(c => ` - ${c.id}`).join('\n')}`
+ return `Error: Cliente "${args.target_id}" no encontrado.\n\nClientes disponibles:\n${clients.map(c => ` - ${c.name || 'Anonymous'} (${c.id})`).join('\n')}`
}
if (args.target_id === status.clientId) {
return 'Error: No puedes transferir la antorcha a ti mismo'
}
+ const targetClient = clients.find(c => c.id === args.target_id)
const success = await transferTorch(args.target_id)
if (success) {
- return `Antorcha transferida a ${args.target_id}. El otro browser ahora tiene control del MCP.`
+ return `Antorcha transferida a ${targetClient?.name || args.target_id}. El otro browser ahora tiene control del MCP.`
} else {
return 'Error al transferir la antorcha'
}
diff --git a/frontend/src/services/torch.ts b/frontend/src/services/torch.ts
index fc03742..8b20f5e 100644
--- a/frontend/src/services/torch.ts
+++ b/frontend/src/services/torch.ts
@@ -22,11 +22,14 @@ function connectToTorchServer(): Promise {
torchWs.onopen = () => {
console.log('[Torch] Connected to server')
- // Register this client
+ const torchStore = useTorchStore()
+ // Register this client with name and autoRequest
torchWs?.send(JSON.stringify({
type: 'register',
userAgent: navigator.userAgent,
- hostname: window.location.hostname
+ hostname: window.location.hostname,
+ name: torchStore.clientName || 'Anonymous',
+ autoRequest: torchStore.autoRequest
}))
resolve()
}
@@ -93,6 +96,12 @@ async function handleMessage(data: any) {
console.log('[Torch] Got torch, connecting to MCP')
await connectToMCP()
}
+
+ // Auto-request: if no one holds the torch and we have autoRequest enabled
+ if (!hasTorchNow && data.holderId === null && torchStore.autoRequest) {
+ console.log('[Torch] Auto-requesting torch (no holder)')
+ requestTorch()
+ }
break
}
@@ -157,6 +166,18 @@ export async function transferTorch(targetId: string): Promise {
return true
}
+/**
+ * Update client name on server
+ */
+export function updateName(name: string): void {
+ const torchStore = useTorchStore()
+ torchStore.setClientName(name)
+
+ if (torchWs?.readyState === WebSocket.OPEN) {
+ torchWs.send(JSON.stringify({ type: 'update-name', name }))
+ }
+}
+
/**
* Get list of connected clients
*/
diff --git a/frontend/src/stores/torch.ts b/frontend/src/stores/torch.ts
index e8d46da..84cc529 100644
--- a/frontend/src/stores/torch.ts
+++ b/frontend/src/stores/torch.ts
@@ -3,6 +3,7 @@ import { ref, computed } from 'vue'
export interface TorchClient {
id: string
+ name: string
userAgent: string
hostname: string
url: string
@@ -17,6 +18,8 @@ export const useTorchStore = defineStore('torch', () => {
const torchHolderId = ref(null)
const clients = ref([])
const isRequesting = ref(false)
+ const clientName = ref(localStorage.getItem('torch-client-name') || '')
+ const autoRequest = ref(localStorage.getItem('torch-auto-request') === 'true')
// Computed
const isConnected = computed(() => clientId.value !== null)
@@ -44,12 +47,23 @@ export const useTorchStore = defineStore('torch', () => {
isRequesting.value = value
}
+ function setClientName(name: string) {
+ clientName.value = name
+ localStorage.setItem('torch-client-name', name)
+ }
+
+ function setAutoRequest(val: boolean) {
+ autoRequest.value = val
+ localStorage.setItem('torch-auto-request', String(val))
+ }
+
function reset() {
clientId.value = null
hasTorch.value = false
torchHolderId.value = null
clients.value = []
isRequesting.value = false
+ // Do NOT reset clientName/autoRequest — persist across sessions
}
return {
@@ -59,6 +73,8 @@ export const useTorchStore = defineStore('torch', () => {
torchHolderId,
clients,
isRequesting,
+ clientName,
+ autoRequest,
// Computed
isConnected,
torchHolder,
@@ -67,6 +83,8 @@ export const useTorchStore = defineStore('torch', () => {
setTorchState,
setClients,
setRequesting,
+ setClientName,
+ setAutoRequest,
reset
}
})
diff --git a/server/services/handlers/torch-handler.ts b/server/services/handlers/torch-handler.ts
index 3ce7924..5d7d804 100644
--- a/server/services/handlers/torch-handler.ts
+++ b/server/services/handlers/torch-handler.ts
@@ -8,6 +8,7 @@
interface TorchClient {
ws: any
id: string
+ name: string
userAgent: string
hostname: string
connectedAt: Date
@@ -27,6 +28,7 @@ function generateClientId(): string {
function broadcastTorchState(broadcast: (message: string, filter?: (ws: any) => boolean) => void) {
const clientList = Array.from(torchClients.values()).map(c => ({
id: c.id,
+ name: c.name,
userAgent: c.userAgent,
hostname: c.hostname,
connectedAt: c.connectedAt.toISOString(),
@@ -50,6 +52,7 @@ export function handleTorchConnect(ws: any, broadcast: (message: string, filter?
torchClients.set(ws, {
ws,
id,
+ name: 'Anonymous',
userAgent: 'Unknown',
hostname: 'Unknown',
connectedAt: new Date()
@@ -68,8 +71,14 @@ export function handleTorchMessage(ws: any, data: any, broadcast: (message: stri
case 'register': {
client.userAgent = data.userAgent || 'Unknown'
client.hostname = data.hostname || 'Unknown'
+ client.name = data.name || 'Anonymous'
+
+ // Auto-grant torch if requested and no one holds it
+ if (data.autoRequest && torchHolderId === null) {
+ torchHolderId = client.id
+ console.log(`[Torch] Auto-granted torch to ${client.name} (${client.id})`)
+ }
- // No auto-assign - torch must be explicitly requested
const hasTorch = torchHolderId === client.id
ws.send(JSON.stringify({
@@ -78,7 +87,15 @@ export function handleTorchMessage(ws: any, data: any, broadcast: (message: stri
hasTorch
}))
- console.log(`[Torch] Registered: ${client.id} (torch: ${hasTorch})`)
+ console.log(`[Torch] Registered: ${client.name} (${client.id}) (torch: ${hasTorch})`)
+ broadcastTorchState(broadcast)
+ break
+ }
+
+ case 'update-name': {
+ const newName = (data.name || '').substring(0, 20) || 'Anonymous'
+ client.name = newName
+ console.log(`[Torch] Name updated: ${client.id} → ${newName}`)
broadcastTorchState(broadcast)
break
}
@@ -88,7 +105,7 @@ export function handleTorchMessage(ws: any, data: any, broadcast: (message: stri
torchHolderId = client.id
ws.send(JSON.stringify({ type: 'granted' }))
- console.log(`[Torch] Transferred: ${previousHolder} → ${client.id}`)
+ console.log(`[Torch] Transferred: ${previousHolder} → ${client.name} (${client.id})`)
broadcastTorchState(broadcast)
break
}
@@ -97,7 +114,7 @@ export function handleTorchMessage(ws: any, data: any, broadcast: (message: stri
if (torchHolderId === client.id) {
torchHolderId = null
ws.send(JSON.stringify({ type: 'released' }))
- console.log(`[Torch] Released by: ${client.id}`)
+ console.log(`[Torch] Released by: ${client.name} (${client.id})`)
broadcastTorchState(broadcast)
}
break
@@ -135,7 +152,7 @@ export function handleTorchMessage(ws: any, data: any, broadcast: (message: stri
torchHolderId = targetId
ws.send(JSON.stringify({ type: 'transferred', targetId }))
- console.log(`[Torch] Transferred by ${client.id}: ${previousHolder} → ${targetId}`)
+ console.log(`[Torch] Transferred by ${client.name} (${client.id}): ${previousHolder} → ${targetId}`)
broadcastTorchState(broadcast)
break
}
@@ -148,7 +165,7 @@ export function handleTorchMessage(ws: any, data: any, broadcast: (message: stri
export function handleTorchDisconnect(ws: any, broadcast: (message: string, filter?: (ws: any) => boolean) => void) {
const client = torchClients.get(ws)
if (client) {
- console.log(`[Torch] Client disconnected: ${client.id}`)
+ console.log(`[Torch] Client disconnected: ${client.name} (${client.id})`)
// If this client had the torch, release it (no auto-assign)
if (torchHolderId === client.id) {
diff --git a/server/services/sync-server.ts b/server/services/sync-server.ts
index 8815534..5080106 100644
--- a/server/services/sync-server.ts
+++ b/server/services/sync-server.ts
@@ -78,7 +78,7 @@ export function startSyncServer() {
const data = JSON.parse(message.toString())
// Route to appropriate handler based on message type
- if (data.type?.startsWith('torch-') || ['register', 'request', 'release', 'transfer'].includes(data.type)) {
+ if (data.type?.startsWith('torch-') || ['register', 'request', 'release', 'transfer', 'update-name'].includes(data.type)) {
handleTorchMessage(ws, data, broadcast)
}
// Git doesn't expect messages from client