remove the desktop-pet system
The pet feature was three loosely coupled subsystems that no core flow depended on: the Petdex catalog page, the pet install/preference store, and the pet-state socket. All three are removed. The only cross-boundary touch point was a try/catch-wrapped observer that mapped chat-run events to pet animations. Removing it leaves the /chat-run event contract unchanged: no event name, field name, field type, or emission timing changed. Removing petdexPublicRoutes also closes the unauthenticated outbound proxy at /api/hermes/petdex/asset, which shrinks the SSRF surface.
This commit is contained in:
@@ -1,74 +0,0 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import {
|
||||
connectPetStateSocket,
|
||||
disconnectPetStateSocket,
|
||||
onPetStateSnapshot,
|
||||
requestPetStateSnapshot,
|
||||
type PetActivity,
|
||||
type PetState,
|
||||
type PetStateSnapshot,
|
||||
} from '@/api/hermes/pet-state'
|
||||
|
||||
function emptySnapshot(profile = 'default'): PetStateSnapshot {
|
||||
return {
|
||||
profile,
|
||||
state: 'idle',
|
||||
activity: {},
|
||||
updatedAt: Date.now(),
|
||||
activeTools: [],
|
||||
awaiting: [],
|
||||
}
|
||||
}
|
||||
|
||||
export const usePetStateStore = defineStore('petState', () => {
|
||||
const snapshot = ref<PetStateSnapshot>(emptySnapshot())
|
||||
const connected = ref(false)
|
||||
const error = ref('')
|
||||
let unsubscribe: (() => void) | null = null
|
||||
|
||||
const state = computed<PetState>(() => snapshot.value.state)
|
||||
const activity = computed<PetActivity>(() => snapshot.value.activity)
|
||||
|
||||
async function connect(profile?: string | null): Promise<void> {
|
||||
disconnect()
|
||||
error.value = ''
|
||||
const socket = connectPetStateSocket(profile)
|
||||
socket.on('connect', () => {
|
||||
connected.value = true
|
||||
error.value = ''
|
||||
})
|
||||
socket.on('disconnect', () => {
|
||||
connected.value = false
|
||||
})
|
||||
socket.on('connect_error', (err: Error) => {
|
||||
connected.value = false
|
||||
error.value = err.message
|
||||
})
|
||||
unsubscribe = onPetStateSnapshot(next => {
|
||||
snapshot.value = next
|
||||
}, profile)
|
||||
try {
|
||||
snapshot.value = await requestPetStateSnapshot(profile)
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : String(err)
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect(): void {
|
||||
unsubscribe?.()
|
||||
unsubscribe = null
|
||||
disconnectPetStateSocket()
|
||||
connected.value = false
|
||||
}
|
||||
|
||||
return {
|
||||
activity,
|
||||
connect,
|
||||
connected,
|
||||
disconnect,
|
||||
error,
|
||||
snapshot,
|
||||
state,
|
||||
}
|
||||
})
|
||||
@@ -1,82 +0,0 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import * as petsApi from '@/api/hermes/pets'
|
||||
import type { ActivePet, WebPetPosition } from '@/api/hermes/pets'
|
||||
|
||||
export const usePetsStore = defineStore('pets', () => {
|
||||
const activePet = ref<ActivePet | null>(null)
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const error = ref('')
|
||||
|
||||
const hasActivePet = computed(() => !!activePet.value?.enabled)
|
||||
|
||||
async function loadActivePet(): Promise<ActivePet | null> {
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
try {
|
||||
activePet.value = await petsApi.fetchActivePet()
|
||||
return activePet.value
|
||||
} catch (err: any) {
|
||||
error.value = err?.message || 'Failed to load active pet'
|
||||
activePet.value = null
|
||||
return null
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function adopt(slug: string): Promise<ActivePet> {
|
||||
saving.value = true
|
||||
error.value = ''
|
||||
try {
|
||||
activePet.value = await petsApi.adoptPet(slug)
|
||||
return activePet.value
|
||||
} catch (err: any) {
|
||||
error.value = err?.message || 'Failed to adopt pet'
|
||||
throw err
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function savePreferences(input: { scale?: number; position?: WebPetPosition; enabled?: boolean }): Promise<void> {
|
||||
if (!activePet.value) return
|
||||
activePet.value = {
|
||||
...activePet.value,
|
||||
...(typeof input.scale === 'number' ? { scale: input.scale } : {}),
|
||||
...(input.position ? { position: input.position } : {}),
|
||||
...(typeof input.enabled === 'boolean' ? { enabled: input.enabled } : {}),
|
||||
}
|
||||
saving.value = true
|
||||
try {
|
||||
const saved = await petsApi.updateActivePetPreferences(input)
|
||||
if (saved) activePet.value = saved
|
||||
else if (input.enabled === false) activePet.value = null
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function hideActivePet(): Promise<void> {
|
||||
await savePreferences({ enabled: false })
|
||||
}
|
||||
|
||||
function clear(): void {
|
||||
activePet.value = null
|
||||
error.value = ''
|
||||
}
|
||||
|
||||
return {
|
||||
activePet,
|
||||
loading,
|
||||
saving,
|
||||
error,
|
||||
hasActivePet,
|
||||
loadActivePet,
|
||||
adopt,
|
||||
savePreferences,
|
||||
hideActivePet,
|
||||
clear,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user