openpetswithchatandmcp/packages/install-pet/codemap.md
2026-05-11 12:31:59 +01:00

2.8 KiB

packages/install-pet/

Standalone pet installer from OpenPets gallery catalog.

Responsibility

Provides a standalone CLI tool for installing pets from the OpenPets gallery catalog. Can operate through the running desktop app (preferred) or directly download and extract pet ZIP files.

Design

Dual Install Modes:

  1. Via Running App: Uses @open-pets/client to request installation through the desktop app
  2. Direct Install: Downloads from catalog, validates, extracts to user data directory

Catalog Integration:

  • URL: https://openpets.dev/pets/catalog.v2.json
  • Validation: Schema version, unique IDs, URL host/path allowlisting
  • Pet structure: { id, displayName, description, preview, zip }

ZIP Handling:

  • Library: yauzl for streaming ZIP extraction
  • Security: Path traversal prevention, symlink rejection, size limits
  • Limits: 50MB download, 200MB extracted, 500 files, 100MB per file
  • Required files: pet.json, spritesheet.webp
  • Extraction: Atomic (temp dir → rename), permission 0o600/0o700

State Management:

  • User data path: Platform-specific (macOS: ~/Library/Application Support/OpenPets, Windows: %APPDATA%/OpenPets, Linux: ~/.config/OpenPets)
  • State file: openpets-state.json
  • Lock file: .install-pet.lock (prevents concurrent installs, 10min stale timeout)
  • Pet directory: pets/<petId>/

Validation:

  • Pet ID: ^[a-z0-9][a-z0-9_-]{0,63}$, excludes "builtin"
  • Catalog URLs: HTTPS only, specific host allowlist
  • ZIP entries: No encryption, supported compression (stored/deflate), valid Unix modes

Flow

installPet({ petId, preferRunningApp })
    ↓
tryInstallThroughRunningApp() → createOpenPetsClient().installPet()
    ↓ (fallback on unavailable/timeout)
installPetDirectly()
    ↓
acquireDirectInstallLock() → mkdir lock, write owner.json
    ↓
fetchCatalog() → GET https://openpets.dev/pets/catalog.v2.json
    ↓
getCatalogPet(petId) → Validate exists in catalog
    ↓
downloadPetZip(zipUrl) → Stream to buffer, validate magic bytes
    ↓
extractPetZip(buffer, tempDir) → yauzl streaming extract
    ↓
validateExtractedPet() → Check pet.json, spritesheet.webp exist
    ↓
rename(tempDir, finalDir) → Atomic move
    ↓
writeInstalledPetState() → Update openpets-state.json
    ↓
releaseLock() → rm lock directory

Integration Points

Dependencies:

  • @open-pets/client - Fallback IPC to running app
  • yauzl - ZIP file handling

External Services:

  • openpets.dev - Catalog JSON and pet metadata
  • zip.openpets.dev - ZIP file downloads

CLI Usage:

  • Binary: install-pet <pet-id>
  • Also invocable via npx -y install-pet <pet-id>

Exports:

  • installPet() - Main install function
  • parseArgs() - CLI argument parsing
  • getOpenPetsUserDataPath() - Platform-specific path resolution
  • validatePetId() - ID format validation