@xopcai/xopc 0.0.126 → 0.0.128
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/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-C70vgt60.js → agent-avatar-dicebear-Dx3HQkOk.js} +1 -1
- package/dist/gateway/static/root/assets/agents-Ceb_WmSZ.js +2 -0
- package/dist/gateway/static/root/assets/app-management-settings-panel-DGtoXVvI.js +1 -0
- package/dist/gateway/static/root/assets/{appearance-settings-BHlahIDL.js → appearance-settings-BQQf_iLu.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-D05daQF8.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-B-aoIt0w.js → archive-plugin-HuCFa2UL.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-BD0G0FD5.js → attachment-load-N3csiRso.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-iJRVI7aZ.js → automation-suggestion-card-DeYEW2YQ.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-Dxn5-hGx.js +4 -0
- package/dist/gateway/static/root/assets/{binary-plugins-B0mNlVSz.js → binary-plugins-DnJO0VG7.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-CJY9Lu5G.js → block-editor-DOAZkqfs.js} +27 -27
- package/dist/gateway/static/root/assets/browser-settings-page-DSB-ZKIX.js +8 -0
- package/dist/gateway/static/root/assets/capability-presets-api-DRCu-s6d.js +1 -0
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-M7jOJxZX.js +1 -0
- package/dist/gateway/static/root/assets/channel-recipient-api-BrW5Kcgc.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-C1iggCG3.js +1 -0
- package/dist/gateway/static/root/assets/{code-editor-gbRgS14t.js → code-editor--tMO38Tr.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-Df0TJNgv.js +2 -0
- package/dist/gateway/static/root/assets/desktop-pet-MnqKnEgd.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-settings-PRnTI9no.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-sprite-DqZ6fbIj.js +1 -0
- package/dist/gateway/static/root/assets/directory-picker-path-field-DJNFtSWq.js +1 -0
- package/dist/gateway/static/root/assets/dreaming-settings-D0_2JavX.js +3 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-BbyczVZ3.js → extension-debug-page-C4sQuETO.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DLN1I3DR.js → extension-page-DhnpWAUJ.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-D7JDxFGh.js +1 -0
- package/dist/gateway/static/root/assets/fetch-BMjlW0g1.js +1 -0
- package/dist/gateway/static/root/assets/gateway-config-api-B_VyPdKh.js +1 -0
- package/dist/gateway/static/root/assets/gateway-settings-C0TutwM5.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-DEMad2wt.js → gateway-startup-retry-CY5W2Pp4.js} +1 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-DrDGK2L5.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-aaIMRRor.js +4 -0
- package/dist/gateway/static/root/assets/goals-config-api-CzTu01a1.js +1 -0
- package/dist/gateway/static/root/assets/goals-page-CBfCPiNK.js +2 -0
- package/dist/gateway/static/root/assets/goals-settings-4rKUC3EH.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-DqJBKlza.js +1 -0
- package/dist/gateway/static/root/assets/{html-workspace-editor-DV90ddkE.js → html-workspace-editor-COqmtESs.js} +1 -1
- package/dist/gateway/static/root/assets/index-38TSE7ng.js +257 -0
- package/dist/gateway/static/root/assets/index-EKmpPvZj.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-lKTg6St7.js → keyboard-shortcuts-settings-ClVhEihC.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-J8fw1O8x.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-editor-BFOjBBla.js → markdown-editor-jD1LPiP-.js} +2 -2
- package/dist/gateway/static/root/assets/{markdown-split-Bv4k6iGJ.js → markdown-split-DQYDAL4r.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-Bbi8g3N8.js → media-plugins-o5fchHfb.js} +1 -1
- package/dist/gateway/static/root/assets/messages-YzjnURPJ.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-D9csuY5T.js +2 -0
- package/dist/gateway/static/root/assets/notes-page-CeWoUGI-.js +1 -0
- package/dist/gateway/static/root/assets/notes-workbench-D_oaF0Pq.js +2 -0
- package/dist/gateway/static/root/assets/{page-tabs-Cp395RYD.js → page-tabs-u0u41Gxz.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-DqV5OM4m.js → preview-open-alternatives-BaYRX55y.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-dkHzwhDL.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-DBp2WExF.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-Cgzg4pNN.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-BItmzAzI.js → save-bar-controls-8ebg7PIX.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-BQ8IjjCn.js → schedule-field-Cd7FPvsJ.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-BRpD6x3d.js → schema-form-Bq9iqPeA.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-vkORZvUB.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BFJsnSvx.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-BKSPQM4R.js → settings-collapsible-section-DaVKl6I7.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-BmyfthNQ.js → settings-form-section-C92Shabe.js} +1 -1
- package/dist/gateway/static/root/assets/settings-loading-skeleton-EJwzElLr.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-BLY6onMK.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-layout-Bls5tfbH.js → settings-page-layout-dTbdUfAB.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-BU0JUWIA.js +1 -0
- package/dist/gateway/static/root/assets/share-preview-page-SgKIyNQ7.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-aiY_QGsl.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-C44tGvK7.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-C1re61FQ.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-DKDAojWM.js → system-settings-panel-CntJCZEg.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-BCCV4Kkp.js → typed-models-lib-CjULd6SF.js} +1 -1
- package/dist/gateway/static/root/assets/url-DFGNSBNT.js +1 -0
- package/dist/gateway/static/root/assets/use-channel-catalog-CPdJ0o0i.js +1 -0
- package/dist/gateway/static/root/assets/user-profile-settings-panel-DBgcbZfp.js +1 -0
- package/dist/gateway/static/root/assets/{utils-DPGVr3tS.js → utils-icmWvHl6.js} +1 -1
- package/dist/gateway/static/root/assets/vendor-codemirror-C1WKjCk5.js +45 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-BfvNUIWI.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-BYvZjBuJ.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-BTkRVNAh.js → workflow-run-setup-panel-DMXd1znx.js} +2 -2
- package/dist/gateway/static/root/assets/{workflow-start-dialog-DAImVtdO.js → workflow-start-dialog-Dff4IHzz.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-0-640fQi.js +18 -0
- package/dist/gateway/static/root/index.html +7 -6
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-instance-gateway.d.ts +5 -0
- package/dist/src/agent/agent-manager.d.ts +17 -0
- package/dist/src/agent/agent-manager.js +125 -8
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/capabilities/definitions.d.ts +167 -0
- package/dist/src/agent/capabilities/definitions.js +175 -0
- package/dist/src/agent/capabilities/definitions.js.map +1 -0
- package/dist/src/agent/capabilities/index.d.ts +2 -0
- package/dist/src/agent/capabilities/index.js +3 -0
- package/dist/src/agent/capabilities/registry.d.ts +10 -0
- package/dist/src/agent/capabilities/registry.js +51 -0
- package/dist/src/agent/capabilities/registry.js.map +1 -0
- package/dist/src/agent/goals/post-turn.js +23 -2
- package/dist/src/agent/goals/post-turn.js.map +1 -1
- package/dist/src/agent/messaging/command-handler.d.ts +4 -0
- package/dist/src/agent/messaging/command-handler.js +6 -0
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/service/process-direct-one-shot.js +5 -3
- package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.js +4 -3
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service.d.ts +4 -0
- package/dist/src/agent/service.js +26 -0
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/skills/hub-lock.d.ts +5 -5
- package/dist/src/agent/skills/hub-lock.js +12 -12
- package/dist/src/agent/skills/hub-lock.js.map +1 -1
- package/dist/src/agent/skills/hub-pull.d.ts +15 -0
- package/dist/src/agent/skills/hub-pull.js +299 -24
- package/dist/src/agent/skills/hub-pull.js.map +1 -1
- package/dist/src/agent/skills/index.js +4 -40
- package/dist/src/agent/skills/index.js.map +1 -1
- package/dist/src/agent/skills/install-target.d.ts +3 -0
- package/dist/src/agent/skills/install-target.js +11 -0
- package/dist/src/agent/skills/install-target.js.map +1 -0
- package/dist/src/agent/skills/managed-store.d.ts +5 -3
- package/dist/src/agent/skills/managed-store.js +10 -9
- package/dist/src/agent/skills/managed-store.js.map +1 -1
- package/dist/src/agent/skills/marketplace/adapters/store/adapter.js +5 -3
- package/dist/src/agent/skills/marketplace/adapters/store/adapter.js.map +1 -1
- package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.d.ts +21 -1
- package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.js +62 -6
- package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.js.map +1 -1
- package/dist/src/agent/skills/marketplace/resolve-adapter.js +1 -1
- package/dist/src/agent/skills/marketplace/resolve-adapter.js.map +1 -1
- package/dist/src/agent/skills/parse-skill-metadata.js +9 -1
- package/dist/src/agent/skills/parse-skill-metadata.js.map +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +2 -2
- package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
- package/dist/src/agent/skills/skill-manager.d.ts +4 -0
- package/dist/src/agent/skills/skill-manager.js +18 -6
- package/dist/src/agent/skills/skill-manager.js.map +1 -1
- package/dist/src/agent/skills/types.d.ts +5 -0
- package/dist/src/agent/skills/workspace-skills-dir.d.ts +1 -0
- package/dist/src/agent/skills/workspace-skills-dir.js +5 -1
- package/dist/src/agent/skills/workspace-skills-dir.js.map +1 -1
- package/dist/src/agent/tools/automation-tool.d.ts +6 -6
- package/dist/src/agent/tools/browser/tool/schemas.d.ts +20 -20
- package/dist/src/agent/tools/composio-tool.d.ts +3 -3
- package/dist/src/agent/tools/desktop-pet-tool.d.ts +24 -0
- package/dist/src/agent/tools/desktop-pet-tool.js +90 -0
- package/dist/src/agent/tools/desktop-pet-tool.js.map +1 -0
- package/dist/src/agent/tools/factory.d.ts +5 -0
- package/dist/src/agent/tools/factory.js +22 -0
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/goal-tool.d.ts +16 -16
- package/dist/src/agent/tools/index.d.ts +2 -0
- package/dist/src/agent/tools/index.js +3 -1
- package/dist/src/agent/tools/skill-install-tool.d.ts +28 -0
- package/dist/src/agent/tools/skill-install-tool.js +81 -0
- package/dist/src/agent/tools/skill-install-tool.js.map +1 -0
- package/dist/src/agent/tools/skill-manage-tool.js +3 -6
- package/dist/src/agent/tools/skill-manage-tool.js.map +1 -1
- package/dist/src/agent/tools/tool-manual-tool.d.ts +2 -2
- package/dist/src/agent/tools/xopc-use-tool.d.ts +5 -5
- package/dist/src/automations/domain/types.d.ts +1 -0
- package/dist/src/automations/service/automation-service.js +9 -0
- package/dist/src/automations/service/automation-service.js.map +1 -1
- package/dist/src/browser/schemas.d.ts +52 -52
- package/dist/src/capabilities/store-connector.d.ts +17 -0
- package/dist/src/capabilities/store-connector.js +191 -0
- package/dist/src/capabilities/store-connector.js.map +1 -0
- package/dist/src/chat-commands/builtins/skill.d.ts +1 -0
- package/dist/src/chat-commands/builtins/skill.js +132 -0
- package/dist/src/chat-commands/builtins/skill.js.map +1 -0
- package/dist/src/chat-commands/context.d.ts +3 -0
- package/dist/src/chat-commands/context.js +2 -0
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/chat-commands/index.d.ts +1 -0
- package/dist/src/chat-commands/index.js +3 -1
- package/dist/src/chat-commands/index.js.map +1 -1
- package/dist/src/chat-commands/types.d.ts +17 -0
- package/dist/src/cli/command-loaders.js +1 -0
- package/dist/src/cli/command-loaders.js.map +1 -1
- package/dist/src/cli/command-manifest.js +4 -0
- package/dist/src/cli/command-manifest.js.map +1 -1
- package/dist/src/cli/commands/connectors.d.ts +4 -0
- package/dist/src/cli/commands/connectors.js +96 -0
- package/dist/src/cli/commands/connectors.js.map +1 -0
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/skills.js +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/schema.js +2 -2
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/connectors/health.js +1 -1
- package/dist/src/connectors/install.js +3 -1
- package/dist/src/connectors/install.js.map +1 -1
- package/dist/src/connectors/materialize.js +2 -0
- package/dist/src/connectors/materialize.js.map +1 -1
- package/dist/src/connectors/types.d.ts +19 -1
- package/dist/src/extensions/install.js +1 -1
- package/dist/src/extensions/types/manifest.d.ts +1 -1
- package/dist/src/extensions/update.js +5 -3
- package/dist/src/extensions/update.js.map +1 -1
- package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
- package/dist/src/gateway/agent-builtin-tools.js +1 -0
- package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/chat-stream/mapper.js +5 -1
- package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
- package/dist/src/gateway/chat-stream/protocol.d.ts +2 -0
- package/dist/src/gateway/hono/lib/config-payload.js +1 -1
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/middleware/auth.d.ts +2 -2
- package/dist/src/gateway/hono/middleware/logger.d.ts +1 -1
- package/dist/src/gateway/hono/middleware/route-errors.d.ts +1 -1
- package/dist/src/gateway/hono/middleware/strict-rate-limit.d.ts +2 -2
- package/dist/src/gateway/hono/routes/capabilities.d.ts +3 -0
- package/dist/src/gateway/hono/routes/capabilities.js +79 -0
- package/dist/src/gateway/hono/routes/capabilities.js.map +1 -0
- package/dist/src/gateway/hono/routes/commands-skills.js +88 -3
- package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
- package/dist/src/gateway/hono/routes/connectors.js +2 -2
- package/dist/src/gateway/hono/routes/goals.js +212 -3
- package/dist/src/gateway/hono/routes/goals.js.map +1 -1
- package/dist/src/gateway/hono/routes/index.js +2 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +10 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/mcp.js +1 -1
- package/dist/src/gateway/hono/routes/mobile.d.ts +3 -0
- package/dist/src/gateway/hono/routes/mobile.js +113 -0
- package/dist/src/gateway/hono/routes/mobile.js.map +1 -0
- package/dist/src/gateway/hono/routes/projects.js +45 -0
- package/dist/src/gateway/hono/routes/projects.js.map +1 -1
- package/dist/src/gateway/hono/routes/workspace.js +4 -1
- package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
- package/dist/src/gateway/hono/sse.d.ts +3 -3
- package/dist/src/gateway/service/marketplace-service.d.ts +23 -1
- package/dist/src/gateway/service/marketplace-service.js +57 -6
- package/dist/src/gateway/service/marketplace-service.js.map +1 -1
- package/dist/src/gateway/service/run-gateway-agent.js +6 -2
- package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
- package/dist/src/gateway/service.d.ts +2 -0
- package/dist/src/gateway/service.js +12 -2
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/goals/goal-contract-draft.d.ts +15 -0
- package/dist/src/goals/goal-contract-draft.js +114 -0
- package/dist/src/goals/goal-contract-draft.js.map +1 -0
- package/dist/src/goals/goal-evidence-review.d.ts +15 -0
- package/dist/src/goals/goal-evidence-review.js +91 -0
- package/dist/src/goals/goal-evidence-review.js.map +1 -0
- package/dist/src/goals/goal-runner.js +3 -1
- package/dist/src/goals/goal-runner.js.map +1 -1
- package/dist/src/goals/goal-service.d.ts +17 -1
- package/dist/src/goals/goal-service.js +44 -3
- package/dist/src/goals/goal-service.js.map +1 -1
- package/dist/src/goals/goal-store.d.ts +18 -1
- package/dist/src/goals/goal-store.js +190 -1
- package/dist/src/goals/goal-store.js.map +1 -1
- package/dist/src/goals/index.d.ts +4 -0
- package/dist/src/goals/index.js +3 -1
- package/dist/src/goals/types.d.ts +42 -0
- package/dist/src/mcp/channel-bridge.js +1 -1
- package/dist/src/mobile/notification-service.d.ts +8 -0
- package/dist/src/mobile/notification-service.js +139 -0
- package/dist/src/mobile/notification-service.js.map +1 -0
- package/dist/src/mobile/notification-store.d.ts +28 -0
- package/dist/src/mobile/notification-store.js +134 -0
- package/dist/src/mobile/notification-store.js.map +1 -0
- package/dist/src/mobile/notification-types.d.ts +37 -0
- package/dist/src/mobile/notification-types.js +11 -0
- package/dist/src/mobile/notification-types.js.map +1 -0
- package/dist/src/pets/factory.d.ts +25 -0
- package/dist/src/pets/factory.js +327 -0
- package/dist/src/pets/factory.js.map +1 -0
- package/dist/src/pets/manifest.d.ts +2 -0
- package/dist/src/pets/manifest.js +16 -0
- package/dist/src/pets/manifest.js.map +1 -0
- package/dist/src/pets/validator.d.ts +12 -0
- package/dist/src/pets/validator.js +175 -0
- package/dist/src/pets/validator.js.map +1 -0
- package/dist/src/projects/project-service.d.ts +2 -0
- package/dist/src/projects/project-service.js +6 -0
- package/dist/src/projects/project-service.js.map +1 -1
- package/dist/src/projects/project-store.d.ts +1 -0
- package/dist/src/projects/project-store.js +14 -7
- package/dist/src/projects/project-store.js.map +1 -1
- package/dist/src/projects/types.d.ts +2 -0
- package/dist/src/session/session-title.js +22 -2
- package/dist/src/session/session-title.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/025_project_pinned_at.sql +5 -0
- package/dist/src/storage/sqlite/migrations/026_goal_contracts.sql +10 -0
- package/dist/src/storage/sqlite/migrations/027_mobile_notifications.sql +43 -0
- package/dist/src/storage/sqlite/migrations/028_goal_evidence_requirements.sql +47 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/storage/sqlite/schema.sql +44 -0
- package/dist/src/tui/tui-startup-resources.js +1 -1
- package/package.json +1 -1
- package/skills/THIRD_PARTY_NOTICES.md +16 -0
- package/skills/creative/algorithmic-art/SKILL.md +37 -0
- package/skills/creative/algorithmic-art/templates/generator-template.js +27 -0
- package/skills/creative/algorithmic-art/templates/viewer.html +27 -0
- package/skills/creative/hatch-pet/SKILL.md +63 -0
- package/skills/documents/doc-coauthoring/SKILL.md +40 -0
- package/skills/documents/doc-coauthoring/references/reader-review.md +9 -0
- package/skills/documents/docx/SKILL.md +40 -0
- package/skills/documents/docx/references/editing-checklist.md +8 -0
- package/skills/documents/docx/scripts/inspect_ooxml.py +36 -0
- package/skills/documents/docx/scripts/render_docx.py +33 -0
- package/skills/documents/pdf/SKILL.md +47 -0
- package/skills/documents/pdf/references/operations.md +16 -0
- package/skills/documents/pdf/scripts/inspect_pdf.py +41 -0
- package/skills/documents/pdf/scripts/render_pdf.py +32 -0
- package/skills/documents/pptx/SKILL.md +41 -0
- package/skills/documents/pptx/references/qa-checklist.md +6 -0
- package/skills/documents/pptx/scripts/inspect_ooxml.py +34 -0
- package/skills/documents/pptx/scripts/render_pptx.py +33 -0
- package/skills/engineering/define-goal/SKILL.md +41 -0
- package/skills/engineering/define-goal/references/objective-rubric.md +12 -0
- package/dist/gateway/static/root/assets/agents-DRYVahI-.js +0 -2
- package/dist/gateway/static/root/assets/app-management-settings-panel-D9-HW6cy.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-BdJ1eQg3.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-D8hYQbkl.js +0 -4
- package/dist/gateway/static/root/assets/browser-settings-page-CiWEO-Iy.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-api-Bgc8ag55.js +0 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-B1nIi0TC.js +0 -1
- package/dist/gateway/static/root/assets/channel-recipient-api-0sauApoC.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-BddPdqVo.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-Ba2GDhSE.js +0 -2
- package/dist/gateway/static/root/assets/directory-picker-path-field-CiEc4KmY.js +0 -1
- package/dist/gateway/static/root/assets/dist-By6Rh2ee.js +0 -1
- package/dist/gateway/static/root/assets/dreaming-settings-4bvpk5wr.js +0 -3
- package/dist/gateway/static/root/assets/extension-settings-page-D9VGHmYJ.js +0 -1
- package/dist/gateway/static/root/assets/fetch-5V96dUz3.js +0 -1
- package/dist/gateway/static/root/assets/gateway-config-api-BtIzObwp.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-KFsGGDhg.js +0 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-4PcF6Sh6.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-B482PSaG.js +0 -1
- package/dist/gateway/static/root/assets/goals-config-api-CVN7-pdM.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-CUdgJuDn.js +0 -2
- package/dist/gateway/static/root/assets/goals-settings-IYCH3-Pi.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-Cf_a2Bmz.js +0 -1
- package/dist/gateway/static/root/assets/index-CA2tR2vm.js +0 -256
- package/dist/gateway/static/root/assets/index-Ds3u0LOk.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-BCporcNH.js +0 -2
- package/dist/gateway/static/root/assets/messages-C22hxE1N.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-D68Pyow7.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-CuUMOiyv.js +0 -1
- package/dist/gateway/static/root/assets/notes-workbench-Dx2N5zTk.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-sB8AzZi3.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-LBnJqWK2.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-Dbj0o6SC.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-DhDZKspB.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BvXLMNL6.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-Cfm9UxAr.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-kQEsTGAs.js +0 -1
- package/dist/gateway/static/root/assets/share-preview-page-DbYZNAPb.js +0 -2
- package/dist/gateway/static/root/assets/shares-settings-BtR4B0kn.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-CJNVOWoK.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-DcWDHrt3.js +0 -2
- package/dist/gateway/static/root/assets/use-channel-catalog-NBqg3iFY.js +0 -1
- package/dist/gateway/static/root/assets/user-profile-settings-panel-suQ2x-LB.js +0 -1
- package/dist/gateway/static/root/assets/vendor-codemirror-BvNgyE1Q.js +0 -45
- package/dist/gateway/static/root/assets/voice-api-key-field-DaJASKFP.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-BrCZpAWh.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-vyJ8wSXm.js +0 -18
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { init_paths_state, resolveStateDir } from "../config/paths-state.js";
|
|
2
|
+
import { DESKTOP_PET_ACTIONS } from "./manifest.js";
|
|
3
|
+
import { validateDesktopPetPackage } from "./validator.js";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import { constants } from "node:fs";
|
|
6
|
+
import { access, mkdir, mkdtemp, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
7
|
+
//#region src/pets/factory.ts
|
|
8
|
+
init_paths_state();
|
|
9
|
+
const FRAME_SIZE = 96;
|
|
10
|
+
const MAX_FRAMES = 10;
|
|
11
|
+
function resolveDesktopPetsDir() {
|
|
12
|
+
return join(resolveStateDir(), "pets");
|
|
13
|
+
}
|
|
14
|
+
function hashString(value) {
|
|
15
|
+
let hash = 2166136261;
|
|
16
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
17
|
+
hash ^= value.charCodeAt(i);
|
|
18
|
+
hash = Math.imul(hash, 16777619);
|
|
19
|
+
}
|
|
20
|
+
return hash >>> 0;
|
|
21
|
+
}
|
|
22
|
+
function slugify(value, fallback) {
|
|
23
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || fallback;
|
|
24
|
+
}
|
|
25
|
+
function normalizePackageId(value) {
|
|
26
|
+
return slugify(value.replace(/^custom:/i, ""), "");
|
|
27
|
+
}
|
|
28
|
+
function titleFromPrompt(prompt) {
|
|
29
|
+
return prompt.replace(/[^\p{L}\p{N}\s_-]+/gu, " ").trim().split(/\s+/).slice(0, 4).join(" ") || "Custom Pet";
|
|
30
|
+
}
|
|
31
|
+
function clampText(value, max) {
|
|
32
|
+
const trimmed = value.trim().replace(/\s+/g, " ");
|
|
33
|
+
return trimmed.length > max ? `${trimmed.slice(0, max - 1)}...` : trimmed;
|
|
34
|
+
}
|
|
35
|
+
function hsl(seed, offset, saturation = 74, lightness = 56) {
|
|
36
|
+
return `hsl(${(seed + offset) % 360} ${saturation}% ${lightness}%)`;
|
|
37
|
+
}
|
|
38
|
+
function paletteFromPrompt(prompt) {
|
|
39
|
+
const seed = hashString(prompt);
|
|
40
|
+
const keyword = prompt.toLowerCase();
|
|
41
|
+
return {
|
|
42
|
+
seed,
|
|
43
|
+
ears: keyword.includes("plant") || keyword.includes("leaf") || keyword.includes("seed") ? "leaf" : keyword.includes("star") || keyword.includes("magic") || keyword.includes("spark") ? "spark" : keyword.includes("robot") || keyword.includes("tech") || keyword.includes("signal") ? "antenna" : "round",
|
|
44
|
+
palette: {
|
|
45
|
+
body: hsl(seed, 0, 72, 53),
|
|
46
|
+
accent: hsl(seed, 34, 82, 63),
|
|
47
|
+
face: hsl(seed, 208, 70, 22),
|
|
48
|
+
screen: hsl(seed, 176, 92, 78),
|
|
49
|
+
cheek: hsl(seed, 318, 86, 72)
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function esc(value) {
|
|
54
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
55
|
+
}
|
|
56
|
+
function desktopPetActionFrameCount(action) {
|
|
57
|
+
if (action === "idle") return 6;
|
|
58
|
+
if (action === "success" || action === "error") return 8;
|
|
59
|
+
return 10;
|
|
60
|
+
}
|
|
61
|
+
function desktopPetActionFps(action) {
|
|
62
|
+
if (action === "idle") return 6;
|
|
63
|
+
if (action === "success" || action === "error") return 12;
|
|
64
|
+
return 10;
|
|
65
|
+
}
|
|
66
|
+
function desktopPetActionLoops(action) {
|
|
67
|
+
return action !== "success" && action !== "error";
|
|
68
|
+
}
|
|
69
|
+
function renderAccessory(action, frame, style) {
|
|
70
|
+
const t = frame / Math.max(1, desktopPetActionFrameCount(action) - 1);
|
|
71
|
+
const bob = Math.sin(t * Math.PI * 2) * 2;
|
|
72
|
+
const { palette } = style;
|
|
73
|
+
if (action === "typing") {
|
|
74
|
+
const glow = frame % 2 === 0 ? 1 : .45;
|
|
75
|
+
return `
|
|
76
|
+
<g transform="translate(55 ${59 + bob})">
|
|
77
|
+
<rect x="1" y="0" width="36" height="25" rx="5" fill="#1e3a8a" stroke="#101827" stroke-width="3"/>
|
|
78
|
+
<path d="M14 8l6 5-6 5" fill="none" stroke="${palette.screen}" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" opacity="${glow}"/>
|
|
79
|
+
<rect x="-1" y="24" width="41" height="10" rx="4" fill="${palette.accent}" stroke="#101827" stroke-width="3"/>
|
|
80
|
+
<rect x="9" y="28" width="5" height="2" rx="1" fill="#e0f2fe" opacity="${glow}"/>
|
|
81
|
+
<rect x="19" y="28" width="5" height="2" rx="1" fill="#e0f2fe" opacity="${1 - glow / 3}"/>
|
|
82
|
+
<rect x="29" y="28" width="5" height="2" rx="1" fill="#e0f2fe" opacity="${glow}"/>
|
|
83
|
+
</g>`;
|
|
84
|
+
}
|
|
85
|
+
if (action === "toolbox") {
|
|
86
|
+
const open = frame % 2 === 0 ? -11 : -3;
|
|
87
|
+
return `
|
|
88
|
+
<g transform="translate(6 ${61 + bob})">
|
|
89
|
+
<rect x="6" y="17" width="36" height="21" rx="6" fill="#fb923c" stroke="#101827" stroke-width="3"/>
|
|
90
|
+
<rect x="11" y="${8 + open / 4}" width="26" height="12" rx="6" fill="#fdba74" stroke="#101827" stroke-width="3" transform="rotate(${open} 11 20)"/>
|
|
91
|
+
<path d="M18 9h12v7H18z" fill="none" stroke="#101827" stroke-width="3"/>
|
|
92
|
+
<rect x="17" y="${10 + open / 2}" width="5" height="17" rx="2" fill="#e5e7eb" stroke="#101827" stroke-width="2" transform="rotate(-24 19 26)"/>
|
|
93
|
+
<rect x="29" y="${8 + open / 2}" width="5" height="17" rx="2" fill="#e5e7eb" stroke="#101827" stroke-width="2" transform="rotate(25 31 24)"/>
|
|
94
|
+
</g>`;
|
|
95
|
+
}
|
|
96
|
+
if (action === "search") return `
|
|
97
|
+
<g transform="translate(58 ${58 + bob}) rotate(${frame % 2 === 0 ? -7 : 4} 18 18)">
|
|
98
|
+
<circle cx="15" cy="14" r="11" fill="#e0f2fe" stroke="${palette.accent}" stroke-width="5"/>
|
|
99
|
+
<rect x="24" y="24" width="17" height="7" rx="3" fill="${palette.accent}" stroke="#101827" stroke-width="3" transform="rotate(43 24 24)"/>
|
|
100
|
+
</g>`;
|
|
101
|
+
if (action === "file") return `
|
|
102
|
+
<g transform="translate(59 ${55 + bob}) rotate(${frame % 2 === 0 ? -5 : 5} 16 20)">
|
|
103
|
+
<path d="M5 1h22l9 9v31H5z" fill="#eff6ff" stroke="#101827" stroke-width="3" stroke-linejoin="round"/>
|
|
104
|
+
<path d="M27 1v10h9" fill="${palette.accent}" stroke="#101827" stroke-width="3" stroke-linejoin="round"/>
|
|
105
|
+
<rect x="12" y="20" width="16" height="3" rx="1.5" fill="${palette.body}"/>
|
|
106
|
+
<rect x="12" y="28" width="12" height="3" rx="1.5" fill="${palette.body}"/>
|
|
107
|
+
</g>`;
|
|
108
|
+
if (action === "terminal") return `
|
|
109
|
+
<g transform="translate(55 ${61 + bob})">
|
|
110
|
+
<rect x="0" y="0" width="39" height="29" rx="7" fill="#111827" stroke="#101827" stroke-width="3"/>
|
|
111
|
+
<path d="M10 10l6 5-6 5" fill="none" stroke="#86efac" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" opacity="${frame % 2 === 0 ? 1 : .4}"/>
|
|
112
|
+
<path d="M20 20h9" stroke="#86efac" stroke-width="3" stroke-linecap="round"/>
|
|
113
|
+
</g>`;
|
|
114
|
+
if (action === "browser") return `
|
|
115
|
+
<g transform="translate(55 ${58 + bob})">
|
|
116
|
+
<rect x="0" y="0" width="40" height="31" rx="7" fill="#e0f2fe" stroke="#101827" stroke-width="3"/>
|
|
117
|
+
<path d="M1 11h38" stroke="${palette.accent}" stroke-width="8"/>
|
|
118
|
+
<circle cx="10" cy="10" r="2" fill="#101827"/>
|
|
119
|
+
<circle cx="17" cy="10" r="2" fill="#101827"/>
|
|
120
|
+
<rect x="11" y="21" width="20" height="4" rx="2" fill="${palette.body}"/>
|
|
121
|
+
</g>`;
|
|
122
|
+
if (action === "success") return `
|
|
123
|
+
<g fill="#facc15" transform="translate(61 ${12 + bob})">
|
|
124
|
+
<path d="M12 0l3 8 8 3-8 3-3 8-3-8-8-3 8-3z"/>
|
|
125
|
+
<path d="M-12 18l2 5 5 2-5 2-2 5-2-5-5-2 5-2z" opacity="0.78"/>
|
|
126
|
+
</g>`;
|
|
127
|
+
if (action === "error") return `
|
|
128
|
+
<g transform="translate(61 ${56 + bob})">
|
|
129
|
+
<path d="M18 0l18 32H0z" fill="#facc15" stroke="#101827" stroke-width="3" stroke-linejoin="round"/>
|
|
130
|
+
<text x="18" y="26" text-anchor="middle" font-family="Arial, sans-serif" font-size="22" font-weight="900" fill="#101827">!</text>
|
|
131
|
+
</g>`;
|
|
132
|
+
return "";
|
|
133
|
+
}
|
|
134
|
+
function renderHead(style) {
|
|
135
|
+
const { palette } = style;
|
|
136
|
+
if (style.ears === "antenna") return `
|
|
137
|
+
<path d="M34 19l-5-10" stroke="#101827" stroke-width="3" stroke-linecap="round"/>
|
|
138
|
+
<circle cx="28" cy="8" r="4" fill="${palette.accent}" stroke="#101827" stroke-width="2"/>
|
|
139
|
+
<path d="M65 18l7-9" stroke="#101827" stroke-width="3" stroke-linecap="round"/>
|
|
140
|
+
<circle cx="74" cy="8" r="4" fill="${palette.accent}" stroke="#101827" stroke-width="2"/>`;
|
|
141
|
+
if (style.ears === "leaf") return `
|
|
142
|
+
<path d="M42 19c-8-13 7-18 16-10-1 11-9 15-16 10z" fill="#84cc16" stroke="#101827" stroke-width="3"/>
|
|
143
|
+
<path d="M48 16l9-8" stroke="#365314" stroke-width="2" stroke-linecap="round"/>`;
|
|
144
|
+
if (style.ears === "spark") return `
|
|
145
|
+
<path d="M45 4l4 10 10 4-10 4-4 10-4-10-10-4 10-4z" fill="#facc15" stroke="#101827" stroke-width="3"/>`;
|
|
146
|
+
return `
|
|
147
|
+
<circle cx="29" cy="22" r="13" fill="${palette.accent}" stroke="#101827" stroke-width="3"/>
|
|
148
|
+
<circle cx="66" cy="22" r="13" fill="${palette.accent}" stroke="#101827" stroke-width="3"/>`;
|
|
149
|
+
}
|
|
150
|
+
function renderFrame(style, action, frame) {
|
|
151
|
+
const t = frame / Math.max(1, desktopPetActionFrameCount(action) - 1);
|
|
152
|
+
const bob = Math.sin(t * Math.PI * 2) * (action === "idle" ? 2.5 : 1.5);
|
|
153
|
+
const tilt = action === "toolbox" ? Math.sin(t * Math.PI * 2) * 2.5 : 0;
|
|
154
|
+
const { palette } = style;
|
|
155
|
+
const face = action === "error" ? "#3f0f18" : palette.face;
|
|
156
|
+
const eye = action === "error" ? "#fca5a5" : palette.screen;
|
|
157
|
+
const mouth = action === "success" ? "M39 45q5 5 10 0" : "M42 46h9";
|
|
158
|
+
const leftArm = action === "typing" ? 23 + frame % 2 * 13 : action === "toolbox" ? 39 + frame % 2 * 10 : 9;
|
|
159
|
+
const rightArm = action === "typing" ? -23 - frame % 2 * 13 : action === "toolbox" ? -39 - frame % 2 * 10 : -9;
|
|
160
|
+
return `
|
|
161
|
+
<g transform="translate(0 ${bob}) rotate(${tilt} 48 76)">
|
|
162
|
+
${renderHead(style)}
|
|
163
|
+
<rect x="21" y="24" width="58" height="45" rx="16" fill="${palette.body}" stroke="#101827" stroke-width="4"/>
|
|
164
|
+
<rect x="32" y="36" width="38" height="23" rx="8" fill="${face}"/>
|
|
165
|
+
<path d="M41 48q3 -5 6 0" fill="none" stroke="${eye}" stroke-width="3" stroke-linecap="round"/>
|
|
166
|
+
<path d="M56 48q3 -5 6 0" fill="none" stroke="${eye}" stroke-width="3" stroke-linecap="round"/>
|
|
167
|
+
<path d="${mouth}" fill="none" stroke="${eye}" stroke-width="3" stroke-linecap="round"/>
|
|
168
|
+
<circle cx="32" cy="56" r="3" fill="${palette.cheek}" opacity="0.72"/>
|
|
169
|
+
<circle cx="69" cy="56" r="3" fill="${palette.cheek}" opacity="0.72"/>
|
|
170
|
+
<rect x="36" y="66" width="32" height="21" rx="8" fill="${palette.body}" stroke="#101827" stroke-width="4"/>
|
|
171
|
+
<path d="M43 77h13" stroke="#ffffff" stroke-width="3" stroke-linecap="round" opacity="0.8"/>
|
|
172
|
+
<rect x="22" y="70" width="13" height="20" rx="7" fill="${palette.body}" stroke="#101827" stroke-width="3" transform="rotate(${leftArm} 29 80)"/>
|
|
173
|
+
<rect x="68" y="70" width="13" height="20" rx="7" fill="${palette.body}" stroke="#101827" stroke-width="3" transform="rotate(${rightArm} 75 80)"/>
|
|
174
|
+
<rect x="39" y="83" width="13" height="8" rx="4" fill="${palette.body}" stroke="#101827" stroke-width="3"/>
|
|
175
|
+
<rect x="55" y="83" width="13" height="8" rx="4" fill="${palette.body}" stroke="#101827" stroke-width="3"/>
|
|
176
|
+
</g>
|
|
177
|
+
${renderAccessory(action, frame, style)}`;
|
|
178
|
+
}
|
|
179
|
+
function renderSpritesheet(prompt) {
|
|
180
|
+
const style = paletteFromPrompt(prompt);
|
|
181
|
+
const width = FRAME_SIZE * MAX_FRAMES;
|
|
182
|
+
const height = FRAME_SIZE * DESKTOP_PET_ACTIONS.length;
|
|
183
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
|
184
|
+
<rect width="${width}" height="${height}" fill="none"/>
|
|
185
|
+
${DESKTOP_PET_ACTIONS.flatMap((action, row) => Array.from({ length: MAX_FRAMES }, (_, frame) => {
|
|
186
|
+
const visible = frame < desktopPetActionFrameCount(action);
|
|
187
|
+
return `<g transform="translate(${frame * FRAME_SIZE} ${row * FRAME_SIZE})">${visible ? renderFrame(style, action, frame) : ""}</g>`;
|
|
188
|
+
})).join("")}
|
|
189
|
+
</svg>`;
|
|
190
|
+
}
|
|
191
|
+
function renderThumbnail(name, prompt) {
|
|
192
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${FRAME_SIZE}" height="${FRAME_SIZE}" viewBox="0 0 ${FRAME_SIZE} ${FRAME_SIZE}">
|
|
193
|
+
<rect width="${FRAME_SIZE}" height="${FRAME_SIZE}" rx="20" fill="none"/>
|
|
194
|
+
${renderFrame(paletteFromPrompt(prompt), "idle", 0)}
|
|
195
|
+
<title>${esc(name)}</title>
|
|
196
|
+
</svg>`;
|
|
197
|
+
}
|
|
198
|
+
async function pathExists(path) {
|
|
199
|
+
try {
|
|
200
|
+
await access(path, constants.F_OK);
|
|
201
|
+
return true;
|
|
202
|
+
} catch {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async function resolvePackageDir(targetRoot, id, overwrite) {
|
|
207
|
+
let nextId = id;
|
|
208
|
+
let dir = join(targetRoot, nextId);
|
|
209
|
+
if (overwrite || !await pathExists(dir)) return {
|
|
210
|
+
dir,
|
|
211
|
+
id: nextId
|
|
212
|
+
};
|
|
213
|
+
for (let i = 2; i < 100; i += 1) {
|
|
214
|
+
nextId = `${id}-${i}`;
|
|
215
|
+
dir = join(targetRoot, nextId);
|
|
216
|
+
if (!await pathExists(dir)) return {
|
|
217
|
+
dir,
|
|
218
|
+
id: nextId
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
nextId = `${id}-${Date.now().toString(36)}`;
|
|
222
|
+
return {
|
|
223
|
+
dir: join(targetRoot, nextId),
|
|
224
|
+
id: nextId
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
async function readExistingManifest(dir) {
|
|
228
|
+
try {
|
|
229
|
+
return JSON.parse(await readFile(join(dir, "manifest.json"), "utf8"));
|
|
230
|
+
} catch {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function assertChildPath(root, child) {
|
|
235
|
+
const normalizedRoot = resolve(root);
|
|
236
|
+
const normalizedChild = resolve(child);
|
|
237
|
+
if (normalizedChild !== normalizedRoot && !normalizedChild.startsWith(`${normalizedRoot}\\`) && !normalizedChild.startsWith(`${normalizedRoot}/`)) throw new Error(`Refusing to publish pet package outside target directory: ${normalizedChild}`);
|
|
238
|
+
}
|
|
239
|
+
async function publishPackageDir(targetRoot, tempDir, finalDir, overwrite) {
|
|
240
|
+
assertChildPath(targetRoot, tempDir);
|
|
241
|
+
assertChildPath(targetRoot, finalDir);
|
|
242
|
+
if (!overwrite || !await pathExists(finalDir)) {
|
|
243
|
+
await rename(tempDir, finalDir);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const backupDir = `${finalDir}.backup-${Date.now().toString(36)}`;
|
|
247
|
+
assertChildPath(targetRoot, backupDir);
|
|
248
|
+
await rename(finalDir, backupDir);
|
|
249
|
+
let published = false;
|
|
250
|
+
try {
|
|
251
|
+
await rename(tempDir, finalDir);
|
|
252
|
+
published = true;
|
|
253
|
+
} catch (e) {
|
|
254
|
+
if (!await pathExists(finalDir) && await pathExists(backupDir)) await rename(backupDir, finalDir);
|
|
255
|
+
throw e;
|
|
256
|
+
}
|
|
257
|
+
if (published) await rm(backupDir, {
|
|
258
|
+
recursive: true,
|
|
259
|
+
force: true
|
|
260
|
+
}).catch(() => void 0);
|
|
261
|
+
}
|
|
262
|
+
async function createDesktopPetPackage(input) {
|
|
263
|
+
const prompt = input.prompt.trim();
|
|
264
|
+
if (!prompt) throw new Error("prompt is required");
|
|
265
|
+
const targetRoot = input.targetDir ?? resolveDesktopPetsDir();
|
|
266
|
+
await mkdir(targetRoot, { recursive: true });
|
|
267
|
+
const { id, dir } = await resolvePackageDir(targetRoot, (input.id?.trim() ? normalizePackageId(input.id) : null) || slugify(input.name?.trim() || titleFromPrompt(prompt), `pet-${hashString(prompt).toString(36)}`), input.overwrite === true);
|
|
268
|
+
const existingManifest = input.overwrite === true ? await readExistingManifest(dir) : null;
|
|
269
|
+
const existingName = typeof existingManifest?.name === "string" ? existingManifest.name.trim() : "";
|
|
270
|
+
const baseName = clampText(input.name?.trim() || existingName || titleFromPrompt(prompt), 36);
|
|
271
|
+
const description = clampText(input.description?.trim() || `Custom pet generated from: ${prompt}`, 120);
|
|
272
|
+
const tempDir = await mkdtemp(join(targetRoot, `.tmp-${id}-`));
|
|
273
|
+
const spritesheetPath = join(tempDir, "pet.svg");
|
|
274
|
+
const thumbnailPath = join(tempDir, "thumbnail.svg");
|
|
275
|
+
const manifestPath = join(tempDir, "manifest.json");
|
|
276
|
+
const manifest = {
|
|
277
|
+
id,
|
|
278
|
+
name: baseName,
|
|
279
|
+
description,
|
|
280
|
+
sourcePrompt: prompt,
|
|
281
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
282
|
+
thumbnail: "thumbnail.svg",
|
|
283
|
+
canvasWidth: FRAME_SIZE,
|
|
284
|
+
canvasHeight: FRAME_SIZE,
|
|
285
|
+
animations: Object.fromEntries(DESKTOP_PET_ACTIONS.map((action, row) => [action, {
|
|
286
|
+
src: "pet.svg",
|
|
287
|
+
frameWidth: FRAME_SIZE,
|
|
288
|
+
frameHeight: FRAME_SIZE,
|
|
289
|
+
frameCount: desktopPetActionFrameCount(action),
|
|
290
|
+
fps: desktopPetActionFps(action),
|
|
291
|
+
loop: desktopPetActionLoops(action),
|
|
292
|
+
offsetX: 0,
|
|
293
|
+
offsetY: row * FRAME_SIZE
|
|
294
|
+
}]))
|
|
295
|
+
};
|
|
296
|
+
try {
|
|
297
|
+
await writeFile(spritesheetPath, renderSpritesheet(prompt), "utf8");
|
|
298
|
+
await writeFile(thumbnailPath, renderThumbnail(baseName, prompt), "utf8");
|
|
299
|
+
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
300
|
+
const validation = await validateDesktopPetPackage(tempDir);
|
|
301
|
+
if (validation.ok === false) {
|
|
302
|
+
const detailText = validation.issue.details?.length ? `: ${validation.issue.details.join("; ")}` : "";
|
|
303
|
+
throw new Error(`${validation.issue.reason}${detailText}`);
|
|
304
|
+
}
|
|
305
|
+
await publishPackageDir(targetRoot, tempDir, dir, input.overwrite === true);
|
|
306
|
+
} catch (e) {
|
|
307
|
+
if (await pathExists(tempDir)) await rm(tempDir, {
|
|
308
|
+
recursive: true,
|
|
309
|
+
force: true
|
|
310
|
+
});
|
|
311
|
+
throw e;
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
id,
|
|
315
|
+
name: baseName,
|
|
316
|
+
description,
|
|
317
|
+
dir,
|
|
318
|
+
manifestPath: join(dir, "manifest.json"),
|
|
319
|
+
thumbnailPath: join(dir, "thumbnail.svg"),
|
|
320
|
+
spritesheetPath: join(dir, "pet.svg"),
|
|
321
|
+
sourcePrompt: prompt
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
//#endregion
|
|
325
|
+
export { DESKTOP_PET_ACTIONS, createDesktopPetPackage, desktopPetActionFps, desktopPetActionFrameCount, desktopPetActionLoops, resolveDesktopPetsDir };
|
|
326
|
+
|
|
327
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","names":[],"sources":["../../../src/pets/factory.ts"],"sourcesContent":["import { constants } from 'node:fs';\nimport { access, mkdir, mkdtemp, readFile, rename, rm, writeFile } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\n\nimport { resolveStateDir } from '../config/paths-state.js';\nimport { DESKTOP_PET_ACTIONS, type DesktopPetPackageAction } from './manifest.js';\nimport { validateDesktopPetPackage } from './validator.js';\n\nexport { DESKTOP_PET_ACTIONS, type DesktopPetPackageAction } from './manifest.js';\n\nexport type CreateDesktopPetPackageInput = {\n id?: string;\n name?: string;\n prompt: string;\n description?: string;\n targetDir?: string;\n overwrite?: boolean;\n};\n\nexport type CreateDesktopPetPackageResult = {\n id: string;\n name: string;\n description: string;\n dir: string;\n manifestPath: string;\n thumbnailPath: string;\n spritesheetPath: string;\n sourcePrompt: string;\n};\n\nconst FRAME_SIZE = 96;\nconst MAX_FRAMES = 10;\n\ntype Palette = {\n body: string;\n accent: string;\n face: string;\n screen: string;\n cheek: string;\n};\n\ntype PetStyle = {\n seed: number;\n palette: Palette;\n ears: 'round' | 'antenna' | 'leaf' | 'spark';\n};\n\nexport function resolveDesktopPetsDir(): string {\n return join(resolveStateDir(), 'pets');\n}\n\nfunction hashString(value: string): number {\n let hash = 2166136261;\n for (let i = 0; i < value.length; i += 1) {\n hash ^= value.charCodeAt(i);\n hash = Math.imul(hash, 16777619);\n }\n return hash >>> 0;\n}\n\nfunction slugify(value: string, fallback: string): string {\n const slug = value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9_-]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 48);\n return slug || fallback;\n}\n\nfunction normalizePackageId(value: string): string {\n return slugify(value.replace(/^custom:/i, ''), '');\n}\n\nfunction titleFromPrompt(prompt: string): string {\n const cleaned = prompt\n .replace(/[^\\p{L}\\p{N}\\s_-]+/gu, ' ')\n .trim()\n .split(/\\s+/)\n .slice(0, 4)\n .join(' ');\n return cleaned || 'Custom Pet';\n}\n\nfunction clampText(value: string, max: number): string {\n const trimmed = value.trim().replace(/\\s+/g, ' ');\n return trimmed.length > max ? `${trimmed.slice(0, max - 1)}...` : trimmed;\n}\n\nfunction hsl(seed: number, offset: number, saturation = 74, lightness = 56): string {\n const hue = (seed + offset) % 360;\n return `hsl(${hue} ${saturation}% ${lightness}%)`;\n}\n\nfunction paletteFromPrompt(prompt: string): PetStyle {\n const seed = hashString(prompt);\n const keyword = prompt.toLowerCase();\n const ears = keyword.includes('plant') || keyword.includes('leaf') || keyword.includes('seed')\n ? 'leaf'\n : keyword.includes('star') || keyword.includes('magic') || keyword.includes('spark')\n ? 'spark'\n : keyword.includes('robot') || keyword.includes('tech') || keyword.includes('signal')\n ? 'antenna'\n : 'round';\n return {\n seed,\n ears,\n palette: {\n body: hsl(seed, 0, 72, 53),\n accent: hsl(seed, 34, 82, 63),\n face: hsl(seed, 208, 70, 22),\n screen: hsl(seed, 176, 92, 78),\n cheek: hsl(seed, 318, 86, 72),\n },\n };\n}\n\nfunction esc(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n\nexport function desktopPetActionFrameCount(action: DesktopPetPackageAction): number {\n if (action === 'idle') return 6;\n if (action === 'success' || action === 'error') return 8;\n return 10;\n}\n\nexport function desktopPetActionFps(action: DesktopPetPackageAction): number {\n if (action === 'idle') return 6;\n if (action === 'success' || action === 'error') return 12;\n return 10;\n}\n\nexport function desktopPetActionLoops(action: DesktopPetPackageAction): boolean {\n return action !== 'success' && action !== 'error';\n}\n\nfunction renderAccessory(action: DesktopPetPackageAction, frame: number, style: PetStyle): string {\n const t = frame / Math.max(1, desktopPetActionFrameCount(action) - 1);\n const bob = Math.sin(t * Math.PI * 2) * 2;\n const { palette } = style;\n if (action === 'typing') {\n const glow = frame % 2 === 0 ? 1 : 0.45;\n return `\n <g transform=\"translate(55 ${59 + bob})\">\n <rect x=\"1\" y=\"0\" width=\"36\" height=\"25\" rx=\"5\" fill=\"#1e3a8a\" stroke=\"#101827\" stroke-width=\"3\"/>\n <path d=\"M14 8l6 5-6 5\" fill=\"none\" stroke=\"${palette.screen}\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" opacity=\"${glow}\"/>\n <rect x=\"-1\" y=\"24\" width=\"41\" height=\"10\" rx=\"4\" fill=\"${palette.accent}\" stroke=\"#101827\" stroke-width=\"3\"/>\n <rect x=\"9\" y=\"28\" width=\"5\" height=\"2\" rx=\"1\" fill=\"#e0f2fe\" opacity=\"${glow}\"/>\n <rect x=\"19\" y=\"28\" width=\"5\" height=\"2\" rx=\"1\" fill=\"#e0f2fe\" opacity=\"${1 - glow / 3}\"/>\n <rect x=\"29\" y=\"28\" width=\"5\" height=\"2\" rx=\"1\" fill=\"#e0f2fe\" opacity=\"${glow}\"/>\n </g>`;\n }\n if (action === 'toolbox') {\n const open = frame % 2 === 0 ? -11 : -3;\n return `\n <g transform=\"translate(6 ${61 + bob})\">\n <rect x=\"6\" y=\"17\" width=\"36\" height=\"21\" rx=\"6\" fill=\"#fb923c\" stroke=\"#101827\" stroke-width=\"3\"/>\n <rect x=\"11\" y=\"${8 + open / 4}\" width=\"26\" height=\"12\" rx=\"6\" fill=\"#fdba74\" stroke=\"#101827\" stroke-width=\"3\" transform=\"rotate(${open} 11 20)\"/>\n <path d=\"M18 9h12v7H18z\" fill=\"none\" stroke=\"#101827\" stroke-width=\"3\"/>\n <rect x=\"17\" y=\"${10 + open / 2}\" width=\"5\" height=\"17\" rx=\"2\" fill=\"#e5e7eb\" stroke=\"#101827\" stroke-width=\"2\" transform=\"rotate(-24 19 26)\"/>\n <rect x=\"29\" y=\"${8 + open / 2}\" width=\"5\" height=\"17\" rx=\"2\" fill=\"#e5e7eb\" stroke=\"#101827\" stroke-width=\"2\" transform=\"rotate(25 31 24)\"/>\n </g>`;\n }\n if (action === 'search') {\n return `\n <g transform=\"translate(58 ${58 + bob}) rotate(${frame % 2 === 0 ? -7 : 4} 18 18)\">\n <circle cx=\"15\" cy=\"14\" r=\"11\" fill=\"#e0f2fe\" stroke=\"${palette.accent}\" stroke-width=\"5\"/>\n <rect x=\"24\" y=\"24\" width=\"17\" height=\"7\" rx=\"3\" fill=\"${palette.accent}\" stroke=\"#101827\" stroke-width=\"3\" transform=\"rotate(43 24 24)\"/>\n </g>`;\n }\n if (action === 'file') {\n return `\n <g transform=\"translate(59 ${55 + bob}) rotate(${frame % 2 === 0 ? -5 : 5} 16 20)\">\n <path d=\"M5 1h22l9 9v31H5z\" fill=\"#eff6ff\" stroke=\"#101827\" stroke-width=\"3\" stroke-linejoin=\"round\"/>\n <path d=\"M27 1v10h9\" fill=\"${palette.accent}\" stroke=\"#101827\" stroke-width=\"3\" stroke-linejoin=\"round\"/>\n <rect x=\"12\" y=\"20\" width=\"16\" height=\"3\" rx=\"1.5\" fill=\"${palette.body}\"/>\n <rect x=\"12\" y=\"28\" width=\"12\" height=\"3\" rx=\"1.5\" fill=\"${palette.body}\"/>\n </g>`;\n }\n if (action === 'terminal') {\n return `\n <g transform=\"translate(55 ${61 + bob})\">\n <rect x=\"0\" y=\"0\" width=\"39\" height=\"29\" rx=\"7\" fill=\"#111827\" stroke=\"#101827\" stroke-width=\"3\"/>\n <path d=\"M10 10l6 5-6 5\" fill=\"none\" stroke=\"#86efac\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" opacity=\"${frame % 2 === 0 ? 1 : 0.4}\"/>\n <path d=\"M20 20h9\" stroke=\"#86efac\" stroke-width=\"3\" stroke-linecap=\"round\"/>\n </g>`;\n }\n if (action === 'browser') {\n return `\n <g transform=\"translate(55 ${58 + bob})\">\n <rect x=\"0\" y=\"0\" width=\"40\" height=\"31\" rx=\"7\" fill=\"#e0f2fe\" stroke=\"#101827\" stroke-width=\"3\"/>\n <path d=\"M1 11h38\" stroke=\"${palette.accent}\" stroke-width=\"8\"/>\n <circle cx=\"10\" cy=\"10\" r=\"2\" fill=\"#101827\"/>\n <circle cx=\"17\" cy=\"10\" r=\"2\" fill=\"#101827\"/>\n <rect x=\"11\" y=\"21\" width=\"20\" height=\"4\" rx=\"2\" fill=\"${palette.body}\"/>\n </g>`;\n }\n if (action === 'success') {\n return `\n <g fill=\"#facc15\" transform=\"translate(61 ${12 + bob})\">\n <path d=\"M12 0l3 8 8 3-8 3-3 8-3-8-8-3 8-3z\"/>\n <path d=\"M-12 18l2 5 5 2-5 2-2 5-2-5-5-2 5-2z\" opacity=\"0.78\"/>\n </g>`;\n }\n if (action === 'error') {\n return `\n <g transform=\"translate(61 ${56 + bob})\">\n <path d=\"M18 0l18 32H0z\" fill=\"#facc15\" stroke=\"#101827\" stroke-width=\"3\" stroke-linejoin=\"round\"/>\n <text x=\"18\" y=\"26\" text-anchor=\"middle\" font-family=\"Arial, sans-serif\" font-size=\"22\" font-weight=\"900\" fill=\"#101827\">!</text>\n </g>`;\n }\n return '';\n}\n\nfunction renderHead(style: PetStyle): string {\n const { palette } = style;\n if (style.ears === 'antenna') {\n return `\n <path d=\"M34 19l-5-10\" stroke=\"#101827\" stroke-width=\"3\" stroke-linecap=\"round\"/>\n <circle cx=\"28\" cy=\"8\" r=\"4\" fill=\"${palette.accent}\" stroke=\"#101827\" stroke-width=\"2\"/>\n <path d=\"M65 18l7-9\" stroke=\"#101827\" stroke-width=\"3\" stroke-linecap=\"round\"/>\n <circle cx=\"74\" cy=\"8\" r=\"4\" fill=\"${palette.accent}\" stroke=\"#101827\" stroke-width=\"2\"/>`;\n }\n if (style.ears === 'leaf') {\n return `\n <path d=\"M42 19c-8-13 7-18 16-10-1 11-9 15-16 10z\" fill=\"#84cc16\" stroke=\"#101827\" stroke-width=\"3\"/>\n <path d=\"M48 16l9-8\" stroke=\"#365314\" stroke-width=\"2\" stroke-linecap=\"round\"/>`;\n }\n if (style.ears === 'spark') {\n return `\n <path d=\"M45 4l4 10 10 4-10 4-4 10-4-10-10-4 10-4z\" fill=\"#facc15\" stroke=\"#101827\" stroke-width=\"3\"/>`;\n }\n return `\n <circle cx=\"29\" cy=\"22\" r=\"13\" fill=\"${palette.accent}\" stroke=\"#101827\" stroke-width=\"3\"/>\n <circle cx=\"66\" cy=\"22\" r=\"13\" fill=\"${palette.accent}\" stroke=\"#101827\" stroke-width=\"3\"/>`;\n}\n\nfunction renderFrame(style: PetStyle, action: DesktopPetPackageAction, frame: number): string {\n const t = frame / Math.max(1, desktopPetActionFrameCount(action) - 1);\n const bob = Math.sin(t * Math.PI * 2) * (action === 'idle' ? 2.5 : 1.5);\n const tilt = action === 'toolbox' ? Math.sin(t * Math.PI * 2) * 2.5 : 0;\n const { palette } = style;\n const face = action === 'error' ? '#3f0f18' : palette.face;\n const eye = action === 'error' ? '#fca5a5' : palette.screen;\n const mouth = action === 'success' ? 'M39 45q5 5 10 0' : 'M42 46h9';\n const leftArm = action === 'typing' ? 23 + (frame % 2) * 13 : action === 'toolbox' ? 39 + (frame % 2) * 10 : 9;\n const rightArm = action === 'typing' ? -23 - (frame % 2) * 13 : action === 'toolbox' ? -39 - (frame % 2) * 10 : -9;\n return `\n <g transform=\"translate(0 ${bob}) rotate(${tilt} 48 76)\">\n ${renderHead(style)}\n <rect x=\"21\" y=\"24\" width=\"58\" height=\"45\" rx=\"16\" fill=\"${palette.body}\" stroke=\"#101827\" stroke-width=\"4\"/>\n <rect x=\"32\" y=\"36\" width=\"38\" height=\"23\" rx=\"8\" fill=\"${face}\"/>\n <path d=\"M41 48q3 -5 6 0\" fill=\"none\" stroke=\"${eye}\" stroke-width=\"3\" stroke-linecap=\"round\"/>\n <path d=\"M56 48q3 -5 6 0\" fill=\"none\" stroke=\"${eye}\" stroke-width=\"3\" stroke-linecap=\"round\"/>\n <path d=\"${mouth}\" fill=\"none\" stroke=\"${eye}\" stroke-width=\"3\" stroke-linecap=\"round\"/>\n <circle cx=\"32\" cy=\"56\" r=\"3\" fill=\"${palette.cheek}\" opacity=\"0.72\"/>\n <circle cx=\"69\" cy=\"56\" r=\"3\" fill=\"${palette.cheek}\" opacity=\"0.72\"/>\n <rect x=\"36\" y=\"66\" width=\"32\" height=\"21\" rx=\"8\" fill=\"${palette.body}\" stroke=\"#101827\" stroke-width=\"4\"/>\n <path d=\"M43 77h13\" stroke=\"#ffffff\" stroke-width=\"3\" stroke-linecap=\"round\" opacity=\"0.8\"/>\n <rect x=\"22\" y=\"70\" width=\"13\" height=\"20\" rx=\"7\" fill=\"${palette.body}\" stroke=\"#101827\" stroke-width=\"3\" transform=\"rotate(${leftArm} 29 80)\"/>\n <rect x=\"68\" y=\"70\" width=\"13\" height=\"20\" rx=\"7\" fill=\"${palette.body}\" stroke=\"#101827\" stroke-width=\"3\" transform=\"rotate(${rightArm} 75 80)\"/>\n <rect x=\"39\" y=\"83\" width=\"13\" height=\"8\" rx=\"4\" fill=\"${palette.body}\" stroke=\"#101827\" stroke-width=\"3\"/>\n <rect x=\"55\" y=\"83\" width=\"13\" height=\"8\" rx=\"4\" fill=\"${palette.body}\" stroke=\"#101827\" stroke-width=\"3\"/>\n </g>\n ${renderAccessory(action, frame, style)}`;\n}\n\nfunction renderSpritesheet(prompt: string): string {\n const style = paletteFromPrompt(prompt);\n const width = FRAME_SIZE * MAX_FRAMES;\n const height = FRAME_SIZE * DESKTOP_PET_ACTIONS.length;\n const frames = DESKTOP_PET_ACTIONS.flatMap((action, row) =>\n Array.from({ length: MAX_FRAMES }, (_, frame) => {\n const visible = frame < desktopPetActionFrameCount(action);\n return `<g transform=\"translate(${frame * FRAME_SIZE} ${row * FRAME_SIZE})\">${visible ? renderFrame(style, action, frame) : ''}</g>`;\n }),\n ).join('');\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\" viewBox=\"0 0 ${width} ${height}\">\n <rect width=\"${width}\" height=\"${height}\" fill=\"none\"/>\n ${frames}\n </svg>`;\n}\n\nfunction renderThumbnail(name: string, prompt: string): string {\n const style = paletteFromPrompt(prompt);\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${FRAME_SIZE}\" height=\"${FRAME_SIZE}\" viewBox=\"0 0 ${FRAME_SIZE} ${FRAME_SIZE}\">\n <rect width=\"${FRAME_SIZE}\" height=\"${FRAME_SIZE}\" rx=\"20\" fill=\"none\"/>\n ${renderFrame(style, 'idle', 0)}\n <title>${esc(name)}</title>\n </svg>`;\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function resolvePackageDir(targetRoot: string, id: string, overwrite: boolean): Promise<{ dir: string; id: string }> {\n let nextId = id;\n let dir = join(targetRoot, nextId);\n if (overwrite || !(await pathExists(dir))) return { dir, id: nextId };\n for (let i = 2; i < 100; i += 1) {\n nextId = `${id}-${i}`;\n dir = join(targetRoot, nextId);\n if (!(await pathExists(dir))) return { dir, id: nextId };\n }\n const suffix = Date.now().toString(36);\n nextId = `${id}-${suffix}`;\n return { dir: join(targetRoot, nextId), id: nextId };\n}\n\nasync function readExistingManifest(dir: string): Promise<Record<string, unknown> | null> {\n try {\n return JSON.parse(await readFile(join(dir, 'manifest.json'), 'utf8')) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nfunction assertChildPath(root: string, child: string): void {\n const normalizedRoot = resolve(root);\n const normalizedChild = resolve(child);\n if (\n normalizedChild !== normalizedRoot &&\n !normalizedChild.startsWith(`${normalizedRoot}\\\\`) &&\n !normalizedChild.startsWith(`${normalizedRoot}/`)\n ) {\n throw new Error(`Refusing to publish pet package outside target directory: ${normalizedChild}`);\n }\n}\n\nasync function publishPackageDir(targetRoot: string, tempDir: string, finalDir: string, overwrite: boolean): Promise<void> {\n assertChildPath(targetRoot, tempDir);\n assertChildPath(targetRoot, finalDir);\n if (!overwrite || !(await pathExists(finalDir))) {\n await rename(tempDir, finalDir);\n return;\n }\n\n const backupDir = `${finalDir}.backup-${Date.now().toString(36)}`;\n assertChildPath(targetRoot, backupDir);\n await rename(finalDir, backupDir);\n let published = false;\n try {\n await rename(tempDir, finalDir);\n published = true;\n } catch (e) {\n if (!(await pathExists(finalDir)) && (await pathExists(backupDir))) {\n await rename(backupDir, finalDir);\n }\n throw e;\n }\n if (published) {\n await rm(backupDir, { recursive: true, force: true }).catch(() => undefined);\n }\n}\n\nexport async function createDesktopPetPackage(\n input: CreateDesktopPetPackageInput,\n): Promise<CreateDesktopPetPackageResult> {\n const prompt = input.prompt.trim();\n if (!prompt) {\n throw new Error('prompt is required');\n }\n const targetRoot = input.targetDir ?? resolveDesktopPetsDir();\n await mkdir(targetRoot, { recursive: true });\n const requestedId = input.id?.trim() ? normalizePackageId(input.id) : null;\n const initialId = requestedId || slugify(input.name?.trim() || titleFromPrompt(prompt), `pet-${hashString(prompt).toString(36)}`);\n const { id, dir } = await resolvePackageDir(\n targetRoot,\n initialId,\n input.overwrite === true,\n );\n const existingManifest = input.overwrite === true ? await readExistingManifest(dir) : null;\n const existingName = typeof existingManifest?.name === 'string' ? existingManifest.name.trim() : '';\n const baseName = clampText(input.name?.trim() || existingName || titleFromPrompt(prompt), 36);\n const description = clampText(\n input.description?.trim() || `Custom pet generated from: ${prompt}`,\n 120,\n );\n\n const tempDir = await mkdtemp(join(targetRoot, `.tmp-${id}-`));\n const spritesheetPath = join(tempDir, 'pet.svg');\n const thumbnailPath = join(tempDir, 'thumbnail.svg');\n const manifestPath = join(tempDir, 'manifest.json');\n const manifest = {\n id,\n name: baseName,\n description,\n sourcePrompt: prompt,\n generatedAt: new Date().toISOString(),\n thumbnail: 'thumbnail.svg',\n canvasWidth: FRAME_SIZE,\n canvasHeight: FRAME_SIZE,\n animations: Object.fromEntries(\n DESKTOP_PET_ACTIONS.map((action, row) => [\n action,\n {\n src: 'pet.svg',\n frameWidth: FRAME_SIZE,\n frameHeight: FRAME_SIZE,\n frameCount: desktopPetActionFrameCount(action),\n fps: desktopPetActionFps(action),\n loop: desktopPetActionLoops(action),\n offsetX: 0,\n offsetY: row * FRAME_SIZE,\n },\n ]),\n ),\n };\n\n try {\n await writeFile(spritesheetPath, renderSpritesheet(prompt), 'utf8');\n await writeFile(thumbnailPath, renderThumbnail(baseName, prompt), 'utf8');\n await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\\n`, 'utf8');\n\n const validation = await validateDesktopPetPackage(tempDir);\n if (validation.ok === false) {\n const detailText = validation.issue.details?.length ? `: ${validation.issue.details.join('; ')}` : '';\n throw new Error(`${validation.issue.reason}${detailText}`);\n }\n await publishPackageDir(targetRoot, tempDir, dir, input.overwrite === true);\n } catch (e) {\n if (await pathExists(tempDir)) {\n await rm(tempDir, { recursive: true, force: true });\n }\n throw e;\n }\n\n return {\n id,\n name: baseName,\n description,\n dir,\n manifestPath: join(dir, 'manifest.json'),\n thumbnailPath: join(dir, 'thumbnail.svg'),\n spritesheetPath: join(dir, 'pet.svg'),\n sourcePrompt: prompt,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/pets/manifest.ts
|
|
2
|
+
const DESKTOP_PET_ACTIONS = [
|
|
3
|
+
"idle",
|
|
4
|
+
"typing",
|
|
5
|
+
"toolbox",
|
|
6
|
+
"search",
|
|
7
|
+
"file",
|
|
8
|
+
"terminal",
|
|
9
|
+
"browser",
|
|
10
|
+
"success",
|
|
11
|
+
"error"
|
|
12
|
+
];
|
|
13
|
+
//#endregion
|
|
14
|
+
export { DESKTOP_PET_ACTIONS };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","names":[],"sources":["../../../src/pets/manifest.ts"],"sourcesContent":["export const DESKTOP_PET_ACTIONS = [\n 'idle',\n 'typing',\n 'toolbox',\n 'search',\n 'file',\n 'terminal',\n 'browser',\n 'success',\n 'error',\n] as const;\n\nexport type DesktopPetPackageAction = (typeof DESKTOP_PET_ACTIONS)[number];\n"],"mappings":";AAAA,MAAa,sBAAsB;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type DesktopPetValidationIssue = {
|
|
2
|
+
dir: string;
|
|
3
|
+
reason: string;
|
|
4
|
+
details?: string[];
|
|
5
|
+
};
|
|
6
|
+
export type DesktopPetValidationResult = {
|
|
7
|
+
ok: true;
|
|
8
|
+
} | {
|
|
9
|
+
ok: false;
|
|
10
|
+
issue: DesktopPetValidationIssue;
|
|
11
|
+
};
|
|
12
|
+
export declare function validateDesktopPetPackage(dir: string): Promise<DesktopPetValidationResult>;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { DESKTOP_PET_ACTIONS } from "./manifest.js";
|
|
2
|
+
import { extname, join, resolve } from "node:path";
|
|
3
|
+
import { constants } from "node:fs";
|
|
4
|
+
import { access, readFile } from "node:fs/promises";
|
|
5
|
+
//#region src/pets/validator.ts
|
|
6
|
+
function safeResolve(root, child) {
|
|
7
|
+
if (typeof child !== "string" || !child.trim()) return null;
|
|
8
|
+
const normalizedRoot = resolve(root);
|
|
9
|
+
const candidate = resolve(root, child);
|
|
10
|
+
if (candidate === normalizedRoot) return candidate;
|
|
11
|
+
if (candidate.startsWith(`${normalizedRoot}\\`) || candidate.startsWith(`${normalizedRoot}/`)) return candidate;
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
function positiveNumber(value) {
|
|
15
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
16
|
+
}
|
|
17
|
+
function positiveRoundedNumber(value) {
|
|
18
|
+
const n = positiveNumber(value);
|
|
19
|
+
if (n === null) return null;
|
|
20
|
+
const rounded = Math.round(n);
|
|
21
|
+
return rounded > 0 ? rounded : null;
|
|
22
|
+
}
|
|
23
|
+
function nonNegativeNumber(value, fallback) {
|
|
24
|
+
if (value === void 0) return fallback;
|
|
25
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
26
|
+
}
|
|
27
|
+
function supportedImagePath(path) {
|
|
28
|
+
return [
|
|
29
|
+
".png",
|
|
30
|
+
".jpg",
|
|
31
|
+
".jpeg",
|
|
32
|
+
".gif",
|
|
33
|
+
".webp",
|
|
34
|
+
".svg"
|
|
35
|
+
].includes(extname(path).toLowerCase());
|
|
36
|
+
}
|
|
37
|
+
async function readableImage(path) {
|
|
38
|
+
if (!supportedImagePath(path)) return false;
|
|
39
|
+
try {
|
|
40
|
+
await access(path, constants.R_OK);
|
|
41
|
+
return true;
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function parseSvgDimension(value) {
|
|
47
|
+
if (!value) return null;
|
|
48
|
+
const match = value.trim().match(/^(\d+(?:\.\d+)?)/);
|
|
49
|
+
if (!match) return null;
|
|
50
|
+
const n = Number(match[1]);
|
|
51
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
52
|
+
}
|
|
53
|
+
function parseSvgBounds(svg) {
|
|
54
|
+
const rootMatch = svg.match(/<svg\b[^>]*>/i);
|
|
55
|
+
if (!rootMatch) return null;
|
|
56
|
+
const root = rootMatch[0];
|
|
57
|
+
const width = parseSvgDimension(root.match(/\bwidth=["']([^"']+)["']/i)?.[1]);
|
|
58
|
+
const height = parseSvgDimension(root.match(/\bheight=["']([^"']+)["']/i)?.[1]);
|
|
59
|
+
if (width && height) return {
|
|
60
|
+
width,
|
|
61
|
+
height
|
|
62
|
+
};
|
|
63
|
+
const viewBox = root.match(/\bviewBox=["']([^"']+)["']/i)?.[1]?.trim();
|
|
64
|
+
if (!viewBox) return null;
|
|
65
|
+
const parts = viewBox.split(/[\s,]+/).map(Number);
|
|
66
|
+
if (parts.length !== 4 || parts.some((part) => !Number.isFinite(part))) return null;
|
|
67
|
+
const [, , viewBoxWidth, viewBoxHeight] = parts;
|
|
68
|
+
return viewBoxWidth > 0 && viewBoxHeight > 0 ? {
|
|
69
|
+
width: viewBoxWidth,
|
|
70
|
+
height: viewBoxHeight
|
|
71
|
+
} : null;
|
|
72
|
+
}
|
|
73
|
+
async function imageBounds(path) {
|
|
74
|
+
if (extname(path).toLowerCase() !== ".svg") return null;
|
|
75
|
+
try {
|
|
76
|
+
return parseSvgBounds(await readFile(path, "utf8"));
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function parseAnimation(action, raw, details) {
|
|
82
|
+
if (!raw || typeof raw !== "object") {
|
|
83
|
+
details.push(`${action}: animation must be an object`);
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const obj = raw;
|
|
87
|
+
const src = typeof obj.src === "string" && obj.src.trim() ? obj.src.trim() : null;
|
|
88
|
+
const frameWidth = positiveRoundedNumber(obj.frameWidth);
|
|
89
|
+
const frameHeight = positiveRoundedNumber(obj.frameHeight);
|
|
90
|
+
const frameCount = positiveRoundedNumber(obj.frameCount);
|
|
91
|
+
const fps = positiveRoundedNumber(obj.fps);
|
|
92
|
+
const offsetX = nonNegativeNumber(obj.offsetX, 0);
|
|
93
|
+
const offsetY = nonNegativeNumber(obj.offsetY, 0);
|
|
94
|
+
if (!src) details.push(`${action}: src is required`);
|
|
95
|
+
if (!frameWidth || !frameHeight || !frameCount) details.push(`${action}: frameWidth, frameHeight, and frameCount must be positive numbers`);
|
|
96
|
+
if (!fps) details.push(`${action}: fps must be a positive number`);
|
|
97
|
+
if (offsetX === null || offsetY === null) details.push(`${action}: offsetX and offsetY must be non-negative numbers`);
|
|
98
|
+
if (!src || !frameWidth || !frameHeight || !frameCount || !fps || offsetX === null || offsetY === null) return null;
|
|
99
|
+
return {
|
|
100
|
+
src,
|
|
101
|
+
frameWidth,
|
|
102
|
+
frameHeight,
|
|
103
|
+
frameCount,
|
|
104
|
+
fps,
|
|
105
|
+
offsetX: Math.round(offsetX),
|
|
106
|
+
offsetY: Math.round(offsetY)
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function estimateSheetBounds(animations) {
|
|
110
|
+
const bounds = /* @__PURE__ */ new Map();
|
|
111
|
+
for (const animation of animations) {
|
|
112
|
+
const width = animation.offsetX + animation.frameWidth * animation.frameCount;
|
|
113
|
+
const height = animation.offsetY + animation.frameHeight;
|
|
114
|
+
const existing = bounds.get(animation.src);
|
|
115
|
+
bounds.set(animation.src, {
|
|
116
|
+
width: Math.max(existing?.width ?? 0, width),
|
|
117
|
+
height: Math.max(existing?.height ?? 0, height)
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return bounds;
|
|
121
|
+
}
|
|
122
|
+
async function validateDesktopPetPackage(dir) {
|
|
123
|
+
const details = [];
|
|
124
|
+
let manifest;
|
|
125
|
+
try {
|
|
126
|
+
manifest = JSON.parse(await readFile(join(dir, "manifest.json"), "utf8"));
|
|
127
|
+
} catch (e) {
|
|
128
|
+
return {
|
|
129
|
+
ok: false,
|
|
130
|
+
issue: {
|
|
131
|
+
dir,
|
|
132
|
+
reason: "manifest.json is missing or unreadable",
|
|
133
|
+
details: [e instanceof Error ? e.message : String(e)]
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (typeof manifest.id !== "string" || !manifest.id.trim()) details.push("id is required");
|
|
138
|
+
if (typeof manifest.name !== "string" || !manifest.name.trim()) details.push("name is required");
|
|
139
|
+
if (typeof manifest.description !== "string" || !manifest.description.trim()) details.push("description is required");
|
|
140
|
+
if (positiveNumber(manifest.canvasWidth) === null || positiveNumber(manifest.canvasHeight) === null) details.push("canvasWidth and canvasHeight must be positive numbers");
|
|
141
|
+
const thumbnailPath = safeResolve(dir, manifest.thumbnail);
|
|
142
|
+
if (!thumbnailPath || !await readableImage(thumbnailPath)) details.push("thumbnail is required and must be a readable supported image");
|
|
143
|
+
const rawAnimations = manifest.animations && typeof manifest.animations === "object" ? manifest.animations : null;
|
|
144
|
+
if (!rawAnimations) details.push("animations is required");
|
|
145
|
+
const animations = [];
|
|
146
|
+
if (rawAnimations) for (const action of DESKTOP_PET_ACTIONS) {
|
|
147
|
+
const animation = parseAnimation(action, rawAnimations[action], details);
|
|
148
|
+
if (animation) animations.push(animation);
|
|
149
|
+
}
|
|
150
|
+
for (const animation of animations) {
|
|
151
|
+
const sourcePath = safeResolve(dir, animation.src);
|
|
152
|
+
if (!sourcePath || !await readableImage(sourcePath)) details.push(`${animation.src}: image is missing, outside the pet folder, or uses an unsupported format`);
|
|
153
|
+
}
|
|
154
|
+
const boundsBySrc = estimateSheetBounds(animations);
|
|
155
|
+
for (const [src, requiredBounds] of boundsBySrc) {
|
|
156
|
+
const sourcePath = safeResolve(dir, src);
|
|
157
|
+
if (!sourcePath) continue;
|
|
158
|
+
const actualBounds = await imageBounds(sourcePath);
|
|
159
|
+
if (!actualBounds) continue;
|
|
160
|
+
if (requiredBounds.width > actualBounds.width || requiredBounds.height > actualBounds.height) details.push(`${src}: manifest needs ${requiredBounds.width}x${requiredBounds.height}, but image is ${actualBounds.width}x${actualBounds.height}`);
|
|
161
|
+
}
|
|
162
|
+
if (details.length > 0) return {
|
|
163
|
+
ok: false,
|
|
164
|
+
issue: {
|
|
165
|
+
dir,
|
|
166
|
+
reason: "Invalid desktop pet package",
|
|
167
|
+
details
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
return { ok: true };
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
export { validateDesktopPetPackage };
|
|
174
|
+
|
|
175
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","names":[],"sources":["../../../src/pets/validator.ts"],"sourcesContent":["import { constants } from 'node:fs';\nimport { access, readFile } from 'node:fs/promises';\nimport { extname, join, resolve } from 'node:path';\n\nimport { DESKTOP_PET_ACTIONS, type DesktopPetPackageAction } from './manifest.js';\n\nexport type DesktopPetValidationIssue = {\n dir: string;\n reason: string;\n details?: string[];\n};\n\nexport type DesktopPetValidationResult =\n | { ok: true }\n | { ok: false; issue: DesktopPetValidationIssue };\n\ntype ManifestAnimation = {\n src: string;\n frameWidth: number;\n frameHeight: number;\n frameCount: number;\n fps: number;\n offsetX: number;\n offsetY: number;\n};\n\nfunction safeResolve(root: string, child: unknown): string | null {\n if (typeof child !== 'string' || !child.trim()) return null;\n const normalizedRoot = resolve(root);\n const candidate = resolve(root, child);\n if (candidate === normalizedRoot) return candidate;\n if (candidate.startsWith(`${normalizedRoot}\\\\`) || candidate.startsWith(`${normalizedRoot}/`)) return candidate;\n return null;\n}\n\nfunction positiveNumber(value: unknown): number | null {\n return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : null;\n}\n\nfunction positiveRoundedNumber(value: unknown): number | null {\n const n = positiveNumber(value);\n if (n === null) return null;\n const rounded = Math.round(n);\n return rounded > 0 ? rounded : null;\n}\n\nfunction nonNegativeNumber(value: unknown, fallback: number): number | null {\n if (value === undefined) return fallback;\n return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : null;\n}\n\nfunction supportedImagePath(path: string): boolean {\n return ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg'].includes(extname(path).toLowerCase());\n}\n\nasync function readableImage(path: string): Promise<boolean> {\n if (!supportedImagePath(path)) return false;\n try {\n await access(path, constants.R_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction parseSvgDimension(value: string | undefined): number | null {\n if (!value) return null;\n const match = value.trim().match(/^(\\d+(?:\\.\\d+)?)/);\n if (!match) return null;\n const n = Number(match[1]);\n return Number.isFinite(n) && n > 0 ? n : null;\n}\n\nfunction parseSvgBounds(svg: string): { width: number; height: number } | null {\n const rootMatch = svg.match(/<svg\\b[^>]*>/i);\n if (!rootMatch) return null;\n const root = rootMatch[0];\n const width = parseSvgDimension(root.match(/\\bwidth=[\"']([^\"']+)[\"']/i)?.[1]);\n const height = parseSvgDimension(root.match(/\\bheight=[\"']([^\"']+)[\"']/i)?.[1]);\n if (width && height) return { width, height };\n const viewBox = root.match(/\\bviewBox=[\"']([^\"']+)[\"']/i)?.[1]?.trim();\n if (!viewBox) return null;\n const parts = viewBox.split(/[\\s,]+/).map(Number);\n if (parts.length !== 4 || parts.some((part) => !Number.isFinite(part))) return null;\n const [, , viewBoxWidth, viewBoxHeight] = parts;\n return viewBoxWidth > 0 && viewBoxHeight > 0 ? { width: viewBoxWidth, height: viewBoxHeight } : null;\n}\n\nasync function imageBounds(path: string): Promise<{ width: number; height: number } | null> {\n if (extname(path).toLowerCase() !== '.svg') return null;\n try {\n return parseSvgBounds(await readFile(path, 'utf8'));\n } catch {\n return null;\n }\n}\n\nfunction parseAnimation(action: DesktopPetPackageAction, raw: unknown, details: string[]): ManifestAnimation | null {\n if (!raw || typeof raw !== 'object') {\n details.push(`${action}: animation must be an object`);\n return null;\n }\n const obj = raw as Record<string, unknown>;\n const src = typeof obj.src === 'string' && obj.src.trim() ? obj.src.trim() : null;\n const frameWidth = positiveRoundedNumber(obj.frameWidth);\n const frameHeight = positiveRoundedNumber(obj.frameHeight);\n const frameCount = positiveRoundedNumber(obj.frameCount);\n const fps = positiveRoundedNumber(obj.fps);\n const offsetX = nonNegativeNumber(obj.offsetX, 0);\n const offsetY = nonNegativeNumber(obj.offsetY, 0);\n if (!src) details.push(`${action}: src is required`);\n if (!frameWidth || !frameHeight || !frameCount) {\n details.push(`${action}: frameWidth, frameHeight, and frameCount must be positive numbers`);\n }\n if (!fps) details.push(`${action}: fps must be a positive number`);\n if (offsetX === null || offsetY === null) details.push(`${action}: offsetX and offsetY must be non-negative numbers`);\n if (!src || !frameWidth || !frameHeight || !frameCount || !fps || offsetX === null || offsetY === null) {\n return null;\n }\n return {\n src,\n frameWidth,\n frameHeight,\n frameCount,\n fps,\n offsetX: Math.round(offsetX),\n offsetY: Math.round(offsetY),\n };\n}\n\nfunction estimateSheetBounds(animations: ManifestAnimation[]): Map<string, { width: number; height: number }> {\n const bounds = new Map<string, { width: number; height: number }>();\n for (const animation of animations) {\n const width = animation.offsetX + animation.frameWidth * animation.frameCount;\n const height = animation.offsetY + animation.frameHeight;\n const existing = bounds.get(animation.src);\n bounds.set(animation.src, {\n width: Math.max(existing?.width ?? 0, width),\n height: Math.max(existing?.height ?? 0, height),\n });\n }\n return bounds;\n}\n\nexport async function validateDesktopPetPackage(dir: string): Promise<DesktopPetValidationResult> {\n const details: string[] = [];\n let manifest: Record<string, unknown>;\n try {\n manifest = JSON.parse(await readFile(join(dir, 'manifest.json'), 'utf8')) as Record<string, unknown>;\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n return { ok: false, issue: { dir, reason: 'manifest.json is missing or unreadable', details: [message] } };\n }\n\n if (typeof manifest.id !== 'string' || !manifest.id.trim()) details.push('id is required');\n if (typeof manifest.name !== 'string' || !manifest.name.trim()) details.push('name is required');\n if (typeof manifest.description !== 'string' || !manifest.description.trim()) {\n details.push('description is required');\n }\n if (positiveNumber(manifest.canvasWidth) === null || positiveNumber(manifest.canvasHeight) === null) {\n details.push('canvasWidth and canvasHeight must be positive numbers');\n }\n\n const thumbnailPath = safeResolve(dir, manifest.thumbnail);\n if (!thumbnailPath || !(await readableImage(thumbnailPath))) {\n details.push('thumbnail is required and must be a readable supported image');\n }\n\n const rawAnimations =\n manifest.animations && typeof manifest.animations === 'object'\n ? (manifest.animations as Record<string, unknown>)\n : null;\n if (!rawAnimations) {\n details.push('animations is required');\n }\n\n const animations: ManifestAnimation[] = [];\n if (rawAnimations) {\n for (const action of DESKTOP_PET_ACTIONS) {\n const animation = parseAnimation(action, rawAnimations[action], details);\n if (animation) animations.push(animation);\n }\n }\n\n for (const animation of animations) {\n const sourcePath = safeResolve(dir, animation.src);\n if (!sourcePath || !(await readableImage(sourcePath))) {\n details.push(`${animation.src}: image is missing, outside the pet folder, or uses an unsupported format`);\n }\n }\n\n const boundsBySrc = estimateSheetBounds(animations);\n for (const [src, requiredBounds] of boundsBySrc) {\n const sourcePath = safeResolve(dir, src);\n if (!sourcePath) continue;\n const actualBounds = await imageBounds(sourcePath);\n if (!actualBounds) continue;\n if (requiredBounds.width > actualBounds.width || requiredBounds.height > actualBounds.height) {\n details.push(\n `${src}: manifest needs ${requiredBounds.width}x${requiredBounds.height}, but image is ${actualBounds.width}x${actualBounds.height}`,\n );\n }\n }\n\n if (details.length > 0) return { ok: false, issue: { dir, reason: 'Invalid desktop pet package', details } };\n return { ok: true };\n}\n"],"mappings":"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"}
|