14 KiB
14 KiB
apps/desktop/src/
Responsibility
Core TypeScript source for the OpenPets desktop application. Organized into: lifecycle management, state persistence, Control Center and pet windows, IPC server, agent integrations, pet installation/management, and declarative plus JavaScript plugin runtimes.
Design
- Modular Controllers: Separate controllers for default pet vs agent pets (lease-based)
- Protocol-First IPC: Versioned JSON protocol over TCP/Unix sockets with token auth
- Defensive I/O: All file operations use temp+rename for atomicity, path traversal validation, symlink checks
- Validation at Boundaries: Catalog, ZIP entries, pet metadata, and IPC params all strictly validated
- Lease Pattern: Agent pets use expiring leases (15s TTL) with heartbeats; default pet is persistent
- Sandboxed Renderers: Control Center loads the Vite React/Tailwind bundle through a hardened BrowserWindow and narrow preload bridge; transparent pet windows and plugin SDK host windows stay separate
- Structured Logging: Scoped logging (app, ipc, lease, pet.*, state, tray, ui) with log rotation and redaction
- Reaction Animation Mapping: User-configurable mapping from reaction types to sprite animation states
- Plugin Runtimes: Plugins use validated manifests, approved permissions, persisted config, safe path checks, declarative timer-triggered actions, or sandboxed JavaScript entry modules through the SDK bridge.
Flow
Main Process Flow:
main.ts
├── lifecycle.ts (app events, cleanup)
├── logger.ts (structured logging init)
├── app-state.ts (state init)
├── plugin-service.ts (plugin state/runtime init, JS host wiring)
├── tray.ts (tray creation)
├── local-ipc.ts (IPC server start)
└── windows.ts (UI handlers)
IPC Request Flow:
local-ipc.ts → parseIpcRequest() → handleRequest()
├── hello/status/pets.list/pets.install
└── lease.acquire/heartbeat/release
└── lease-manager.ts
├── resolveTarget() (default vs explicit pet)
├── onFirstExplicitLease → agent-pet-controller.showAgentPet()
└── onLastExplicitLease → agent-pet-controller.closeAgentPetIfOpen()
└── Logging via logger.ts (ipc, lease scopes)
Pet Display Flow:
pet-window.ts
├── createDefaultPetWindow() / createAgentPetWindow()
├── loadDefaultPetContent() / loadExplicitPetContent()
│ ├── HTML generation with CSS sprite animation
│ ├── reaction-animation-mapping.ts (resolveReactionSpriteState)
│ ├── reaction-messages.ts (pickReactionMessage for bubbles)
│ └── Speech bubbles with status badges
└── pet-preload.cjs (renderer IPC for drag/click-through)
Agent Setup Flow:
windows.ts (IPC handlers)
└── agent-setup.ts
├── detectClaudeCodeStatus() (claude --version, claude mcp list)
├── runAgentSetupAction()
│ ├── configure/replace/remove (MCP commands)
│ ├── install-memory (claude-memory.ts)
│ └── install-hooks/uninstall-hooks/doctor-hooks (@open-pets/claude)
├── OpenCode global config management (@open-pets/opencode)
└── Cursor global MCP config management (@open-pets/cursor)
Pet Installation Flow:
pet-installation.ts
├── installPet()
│ ├── getCatalogPet() → catalog.ts
│ ├── downloadPetZip() → validate ZIP magic
│ ├── extractPetZip() → yauzl with entry validation
│ └── installPetState() → app-state.ts
└── importCodexPet() → codex-pets.ts
Control Center Flow:
tray.ts → openControlCenterWindow(route) → windows.ts
├── hardened BrowserWindow loads Vite renderer or packaged dist/renderer/index.html
├── control-center-preload.cjs exposes page-specific APIs
├── Dashboard snapshot: default pet, catalog, plugin health, update status, activity
└── renderer/src/main.tsx routes Dashboard/Pets/Integrations/Plugins/Settings
Plugin Flow:
main.ts → initializePluginService(userData, defaultPluginPetApi, appVersion, ElectronPluginJsHost).start()
├── plugin-state.ts reads/writes userData/openpets-plugin-state.json
├── plugin-runtime.ts reloads enabled manifests
│ ├── declarative runtime schedules timer triggers
│ ├── plugin-js-host.ts starts hidden sandboxed BrowserWindow hosts for JavaScript plugins
│ └── plugin-sdk-bridge.ts → plugin-pet-api.ts/default-pet-controller plus schedules, storage, commands, status, logs, and restricted network
├── plugin-service.ts orchestrates UI actions, permission confirmation, config validation, install/update/uninstall/load-local, and runtime reloads
└── lifecycle.ts → stopPluginService() on quit
Control Center plugins route:
tray.ts → openControlCenterWindow("plugins") → windows.ts → renderer React app
└── openpets:plugins-* IPC handlers call PluginService methods
Catalog install/update:
plugin-catalog.ts → plugin-catalog-validation.ts
└── plugin-package.ts downloads HTTPS ZIP, validates SHA-256, extracts root manifest only, and installs to userData/plugins/{id}
Local development load:
plugin-local-loader.ts validates selected folder manifest and snapshots only openpets.plugin.json to userData/plugins-dev/{id}
Integration Points
-
Within src/:
main.ts→ all modules (orchestrator), includingElectronPluginJsHostfor JavaScript pluginslocal-ipc.ts↔lease-manager.ts↔agent-pet-controller.tswindows.ts↔app-state.ts,agent-setup.ts,catalog.ts,codex-pets.ts,update-checker.tsfor Control Center route snapshots/actionswindows.ts↔plugin-service.tsfor Control Center plugin UI IPC, plugin commands, and Dashboard plugin healthpet-window.ts↔default-pet-controller.ts,agent-pet-controller.tspet-installation.ts↔app-state.ts,catalog.ts,zip-safety.tsplugin-service.ts↔plugin-state.ts,plugin-runtime.ts,plugin-catalog.ts,plugin-package.ts,plugin-local-loader.ts,plugin-js-host.ts,plugin-sdk-bridge.ts
-
To packages/:
@open-pets/claude:buildClaudeMcpPreview,installClaudeHooks,doctorClaudeHooks, etc.@open-pets/opencode:prepareOpenCodeGlobalSetup,doctorOpenCodeGlobalSetup@open-pets/cursor:planCursorMcpInstall,executeCursorMcpWrite,buildCursorRulesPreview, etc.@open-pets/cli: Version lookup for bundled mode
-
To System:
- File system:
app.getPath("userData"),userData/plugins/,userData/plugins-dev/, plugin storage JSON,~/.codex/pets/,~/.claude/,~/.opencode/ - Network:
fetch()to openpets.dev, GitHub API, plugin catalog athttps://openpets.dev/plugins/catalog.v1.json, plugin ZIPs restricted tohttps://zip.openpets.dev/plugins/ - Processes:
spawn()forclaude,opencode,node
- File system:
Key Modules
Core:
main.ts: Entry, single-instance lock, bootstrap sequence, JavaScript plugin host constructionlifecycle.ts: App event handlers (quit, window-all-closed, second-instance) with logging; stops plugin service, IPC, and pet windows on quitstate.ts: Simple shell pause stateapp-state.ts: Persistent JSON state with V1 schema, atomic writes, reaction animation overridesapp-state-core.ts: Pet scale options, onboarding normalizationlogger.ts: Structured logging with scopes (app, ipc, lease, pet.default, pet.agent, pet.window, state, tray, ui), log rotation, redaction
UI:
tray.ts: Tray icon (nativeImage), context menu builder, update status integration, route-targeted Control Center entries, logs folderwindows.ts: Control Center BrowserWindow factory, Dashboard snapshot, IPC handler registration, route targeting, reaction animation settings, plugin/integration/pet/settings UI IPC endpoints, and scoped internal protocolsassets.ts: Tray icon loading with generated fallbackdisplay.ts: Screen geometry helpers, pet window positioningrenderer/: Vite React/Tailwind Control Center shell for Dashboard, Pets, Integrations, Plugins, and Settings.
Pets:
pet-window.ts: Window creation (transparent, frameless, always-on-top), HTML/CSS generation, sprite animation states, speech bubbles, status badges, transient displaysdefault-pet-controller.ts: Default pet visibility, position persistence, transient reactions, status badges, loggingagent-pet-controller.ts: Lease-triggered pet windows, dismissal tracking, transient displays, status badges, loggingbuilt-in-pet.ts: Built-in pet constantreaction-messages.ts: Message pools for each reaction typereaction-animation-mapping.ts: Reaction-to-animation state mapping, user-configurable overrides, sprite state definitions
IPC:
local-ipc.ts: net.Server implementation, request routing, discovery file management, network security (loopback/private address filtering), logginglocal-ipc-protocol.ts: Protocol constants, request/response types, validation functionslocal-ipc-paths.ts: Platform-specific socket paths and discovery file locationslease-manager.ts: Lease lifecycle (acquire, heartbeat, release, cleanup), target resolution
Installation:
pet-installation.ts: ZIP download, yauzl extraction with safety limits, pet validationpet-paths.ts: Safe path resolution for pet directoriescodex-pets.ts: Import from~/.codex/pets/with validationcodex-pets-core.ts: Codex metadata validation constantscatalog.ts: Remote catalog fetch with V3 pagination support, search, fixture fallbackcatalog-validation.ts: CatalogV2/V3 schema validationzip-safety.ts: ZIP entry path validation (traversal prevention, case collision detection)
Plugins:
plugin-manifest.ts: Manifest V1/V2 schema/types and validation for declarative and JavaScript runtimes, permissions (timer/schedule,pet:speak,pet:reaction,storage,status,commands,network), config schema, timer triggers, entry files, and pet actions.plugin-manifest-reader.ts: Safe manifest reader with realpath/allowed-root checks, root filename enforcement, size limit, and expected id/version matching.plugin-config.ts: Config defaulting, replacement validation, and runtime resolution for string/number config references.plugin-state.ts: Persistent plugin state store (openpets-plugin-state.json) with atomic temp+rename writes, normalized records, approved permissions, config, source, and broken reason.plugin-runtime.ts: Runtime that compiles enabled declarative timer triggers, starts/stops JavaScript plugin hosts, verifies approved permissions, exposes public command/status state, validates actions, schedules cancellable timers, and marks broken plugins on validation/action failure.plugin-pet-api.ts: Narrow adapter from plugin actions to default pet externalsay/reactcontroller calls.plugin-service.ts: Application-facing plugin orchestrator for safe snapshots, enable/disable, config save, command execution, reload, catalog install/update, local load, uninstall, permission prompts, compatibility checks, JavaScript host/SDK bridge integration, and runtime reloads.plugin-catalog.ts: Remote plugin catalog fetch with timeout, redirect rejection, response size cap, cache, and refresh support.plugin-catalog-validation.ts: Catalog V1 schema validation, duplicate id checks, semver/SHA fields, permissions canonicalization, and optional minimum OpenPets version.plugin-package.ts: Catalog plugin package download/install with HTTPS host/path allowlist, SHA-256 verification, ZIP size/entry restrictions, manifest/catalog consistency checks, and safe uninstall path resolution.plugin-local-loader.ts: Developer loader that validates a selected local folder and snapshots only the manifest intoplugins-devwith symlink/path/size protections.plugin-js-host.ts: Sandboxed hidden BrowserWindow host for JavaScript plugin entry modules with per-plugin session partitioning, navigation/window-open hardening, SDK IPC tokening, registration handshake, config listener cleanup, and teardown.plugin-sdk-bridge.ts: Permission-checked JavaScript plugin SDK for pet speech/reactions, one-shot/repeating/daily schedules, storage with quotas, config listeners, commands, status, logs, and HTTPS-only public-host fetch.
Agent Integration:
agent-setup.ts: Claude/OpenCode/Cursor detection, MCP configuration, hooks management, action journalingclaude-memory.ts: Claude instructions file management (~/.claude/openpets.md)update-checker.ts: GitHub release polling, update statusupdate-version.ts: Version parsing and comparison
Tests (excluded from detailed codemap coverage per repository conventions):
- Behavior tests live in
tests/*.test.ts(compiled to.test-dist/tests/) - Contract tests live in
contracts/*.contract.ts(compiled to.test-dist/contracts/) - Runtime checks (
check-*.ts) remain insrc/for packaging/validation (compiled todist/)
Data Flow Summary
| Source | Destination | Data |
|---|---|---|
| Catalog API | catalog.ts |
CatalogV2/V3 JSON with pagination |
| ZIP Download | pet-installation.ts |
Extracted to userData/pets/{id}/ |
app-state.ts |
userData/openpets-state.json |
Atomic JSON writes with reaction animation overrides |
| CLI via IPC | local-ipc.ts |
pet.react, pet.say, lease.* |
lease-manager.ts |
agent-pet-controller.ts |
Show/close agent pets |
windows.ts |
Renderer | State snapshots via IPC invoke |
agent-setup.ts |
Claude/OpenCode/Cursor CLI | MCP add/remove, config writes |
| All modules | logger.ts |
Structured logs to userData/logs/openpets.log |
| Plugin catalog | plugin-catalog.ts/plugin-service.ts |
Discoverable plugin metadata filtered by app version and install state |
| Plugin ZIP/local folder | plugin-package.ts/plugin-local-loader.ts |
Validated manifest snapshot installed under userData/plugins* |
plugin-state.ts |
userData/openpets-plugin-state.json |
Installed plugins, enabled flag, approved permissions, config, broken status |
| Control Center renderer | control-center-preload.cjs/windows.ts |
Narrow Dashboard/Pets/Integrations/Plugins/Settings snapshots and route-targeted actions |
plugin-runtime.ts |
plugin-pet-api.ts/plugin-js-host.ts/plugin-sdk-bridge.ts |
Declarative timers and JavaScript SDK actions on default pet, schedules, storage, commands, status, logs, and network |
| Plugins renderer | windows.ts/plugin-service.ts |
Snapshot, enable, config, command, reload, install/update/uninstall, local-load operations |