@openlife/cli 1.7.3
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/INSTALL.md +266 -0
- package/LICENSE +21 -0
- package/README.md +142 -0
- package/bin/openlife.js +3 -0
- package/dist/admin_panel_server.js +66 -0
- package/dist/cli/AgentManager.js +109 -0
- package/dist/cli/AutonomousInstaller.js +134 -0
- package/dist/cli/DreamOrganizer.js +88 -0
- package/dist/cli/HostInstaller.js +426 -0
- package/dist/cli/InstallBanner.js +16 -0
- package/dist/cli/InstallFlow.js +256 -0
- package/dist/cli/InstallHeadless.js +47 -0
- package/dist/cli/InstallModules.js +148 -0
- package/dist/cli/InstallStateStore.js +75 -0
- package/dist/cli/InstallWizard.js +364 -0
- package/dist/cli/ProfileManager.js +163 -0
- package/dist/cli/SystemInstaller.js +89 -0
- package/dist/cli/WorldClassCommands.js +208 -0
- package/dist/design/DesignMdImporter.js +82 -0
- package/dist/design/DesignMdMode.js +93 -0
- package/dist/design/DesignMdRegistry.js +67 -0
- package/dist/index.js +2575 -0
- package/dist/memory/ConversationMemory.js +33 -0
- package/dist/memory/LocalMemoryProvider.js +86 -0
- package/dist/memory/Mem0Provider.js +16 -0
- package/dist/memory/MemoryNamespacePolicy.js +27 -0
- package/dist/memory/MemoryOrchestrator.js +65 -0
- package/dist/memory/MemoryPromotionFlow.js +32 -0
- package/dist/memory/MemoryProvider.js +2 -0
- package/dist/memory/MemoryProviderRegistry.js +27 -0
- package/dist/memory/MemoryRetentionPolicy.js +60 -0
- package/dist/memory/MempalaceProvider.js +72 -0
- package/dist/memory/OmniMemory.js +106 -0
- package/dist/memory/RedisAgentMemoryProvider.js +16 -0
- package/dist/memory/SessionManager.js +86 -0
- package/dist/memory/ZepGraphitiProvider.js +16 -0
- package/dist/orchestrator/AgentRegistry.js +56 -0
- package/dist/orchestrator/AgentScoring.js +82 -0
- package/dist/orchestrator/AgentTeam.js +22 -0
- package/dist/orchestrator/ArbitrationAgent.js +43 -0
- package/dist/orchestrator/ArbitrationScorecard.js +17 -0
- package/dist/orchestrator/AssetPromotionEngine.js +65 -0
- package/dist/orchestrator/AssetReuseRouter.js +63 -0
- package/dist/orchestrator/BenchmarkEngine.js +75 -0
- package/dist/orchestrator/Brain.js +298 -0
- package/dist/orchestrator/CadenceEngine.js +76 -0
- package/dist/orchestrator/CapabilityRouter.js +36 -0
- package/dist/orchestrator/CommandLanguage.js +27 -0
- package/dist/orchestrator/CommandRouter.js +70 -0
- package/dist/orchestrator/ConsequenceForecaster.js +286 -0
- package/dist/orchestrator/CronManager.js +286 -0
- package/dist/orchestrator/DynamicAgentBuilder.js +48 -0
- package/dist/orchestrator/DynamicAgentExecutor.js +15 -0
- package/dist/orchestrator/EnterpriseAgenticCore.js +276 -0
- package/dist/orchestrator/ExecutionBoard.js +86 -0
- package/dist/orchestrator/ExecutionIntent.js +13 -0
- package/dist/orchestrator/ExecutionModePolicy.js +48 -0
- package/dist/orchestrator/ExecutionRouter.js +9 -0
- package/dist/orchestrator/ExecutionState.js +20 -0
- package/dist/orchestrator/ExecutorHealth.js +86 -0
- package/dist/orchestrator/ExternalCatalogRegistry.js +83 -0
- package/dist/orchestrator/Gatekeeper.js +414 -0
- package/dist/orchestrator/Gateway.js +508 -0
- package/dist/orchestrator/GovernanceConsentStore.js +66 -0
- package/dist/orchestrator/GovernanceLayer.js +179 -0
- package/dist/orchestrator/GovernancePolicyStore.js +53 -0
- package/dist/orchestrator/GovernanceScopeLedger.js +134 -0
- package/dist/orchestrator/GovernanceScopePolicy.js +67 -0
- package/dist/orchestrator/IntentClassifier.js +45 -0
- package/dist/orchestrator/JobLifecycle.js +91 -0
- package/dist/orchestrator/LearningRouter.js +24 -0
- package/dist/orchestrator/MediaManager.js +92 -0
- package/dist/orchestrator/MemoryCuratorAgent.js +41 -0
- package/dist/orchestrator/MissionState.js +155 -0
- package/dist/orchestrator/ModelManager.js +84 -0
- package/dist/orchestrator/OperatingSystem.js +71 -0
- package/dist/orchestrator/OperationalMemoryStore.js +94 -0
- package/dist/orchestrator/OptimizationLoop.js +72 -0
- package/dist/orchestrator/OrchestrationLoop.js +905 -0
- package/dist/orchestrator/OrgStructure.js +88 -0
- package/dist/orchestrator/OutcomeSimulator.js +46 -0
- package/dist/orchestrator/ParallelOrchestrationLoop.js +36 -0
- package/dist/orchestrator/PerformanceScorecard.js +105 -0
- package/dist/orchestrator/PlannerAgent.js +46 -0
- package/dist/orchestrator/ProcessSandbox.js +129 -0
- package/dist/orchestrator/PromotionPipeline.js +74 -0
- package/dist/orchestrator/PromotionReviewGate.js +11 -0
- package/dist/orchestrator/QueueScheduler.js +260 -0
- package/dist/orchestrator/ReleaseGate.js +36 -0
- package/dist/orchestrator/ReleaseWorkflow.js +68 -0
- package/dist/orchestrator/RemotePublisher.js +139 -0
- package/dist/orchestrator/ReuseEngine.js +89 -0
- package/dist/orchestrator/ReviewerAgent.js +49 -0
- package/dist/orchestrator/RoleHandoff.js +65 -0
- package/dist/orchestrator/RuntimeHealthMonitor.js +143 -0
- package/dist/orchestrator/RuntimePolicy.js +105 -0
- package/dist/orchestrator/RuntimeProbe.js +97 -0
- package/dist/orchestrator/RuntimeRegistry.js +73 -0
- package/dist/orchestrator/SandboxPolicy.js +22 -0
- package/dist/orchestrator/SecurityDownloadGuard.js +169 -0
- package/dist/orchestrator/SecurityEventStore.js +58 -0
- package/dist/orchestrator/ServiceCompletionPolicy.js +36 -0
- package/dist/orchestrator/ServiceState.js +195 -0
- package/dist/orchestrator/SkillCreator.js +404 -0
- package/dist/orchestrator/SkillLearningLoop.js +57 -0
- package/dist/orchestrator/SkillManager.js +75 -0
- package/dist/orchestrator/SkillNetwork.js +29 -0
- package/dist/orchestrator/SkillRegistryV2.js +28 -0
- package/dist/orchestrator/SkillScoring.js +70 -0
- package/dist/orchestrator/SquadAutoCreator.js +64 -0
- package/dist/orchestrator/SquadCreator.js +727 -0
- package/dist/orchestrator/SquadRegistry.js +28 -0
- package/dist/orchestrator/SquadRouter.js +33 -0
- package/dist/orchestrator/SquadScoring.js +70 -0
- package/dist/orchestrator/SubagentLifecycle.js +90 -0
- package/dist/orchestrator/SynthesizerAgent.js +48 -0
- package/dist/orchestrator/SystemDoctor.js +224 -0
- package/dist/orchestrator/TaskExecutor.js +422 -0
- package/dist/orchestrator/TeammateBoard.js +61 -0
- package/dist/orchestrator/TestHarness.js +184 -0
- package/dist/orchestrator/VoiceManager.js +203 -0
- package/dist/orchestrator/VoiceRouter.js +89 -0
- package/dist/orchestrator/capability/CapabilityGenesisEngine.js +278 -0
- package/dist/orchestrator/capability/CapabilityPackParser.js +223 -0
- package/dist/orchestrator/capability/CapabilityPackSchema.js +62 -0
- package/dist/orchestrator/capability/CapabilityPackState.js +163 -0
- package/dist/orchestrator/providers/AgentProvider.js +2 -0
- package/dist/orchestrator/providers/CapabilityProvider.js +12 -0
- package/dist/orchestrator/providers/CloudAgentProvider.js +55 -0
- package/dist/orchestrator/providers/CloudSkillProvider.js +55 -0
- package/dist/orchestrator/providers/CloudSquadProvider.js +55 -0
- package/dist/orchestrator/providers/CompositeAgentProvider.js +16 -0
- package/dist/orchestrator/providers/CompositeCapabilityProvider.js +25 -0
- package/dist/orchestrator/providers/CompositeSkillProvider.js +16 -0
- package/dist/orchestrator/providers/CompositeSquadProvider.js +16 -0
- package/dist/orchestrator/providers/CompositeWorkflowProvider.js +46 -0
- package/dist/orchestrator/providers/FileAgentProvider.js +105 -0
- package/dist/orchestrator/providers/FileCapabilityProvider.js +106 -0
- package/dist/orchestrator/providers/FileSkillProvider.js +65 -0
- package/dist/orchestrator/providers/FileSquadProvider.js +69 -0
- package/dist/orchestrator/providers/FileWorkflowProvider.js +103 -0
- package/dist/orchestrator/providers/SkillProvider.js +2 -0
- package/dist/orchestrator/providers/SquadProvider.js +2 -0
- package/dist/orchestrator/toolset/ToolsetGuard.js +69 -0
- package/dist/orchestrator/toolset/ToolsetRegistry.js +65 -0
- package/dist/orchestrator/toolset/ToolsetSchema.js +21 -0
- package/dist/orchestrator/util/AtomicWriter.js +204 -0
- package/dist/orchestrator/util/DistributedLock.js +232 -0
- package/dist/orchestrator/util/TemplateRenderer.js +87 -0
- package/dist/orchestrator/util/WatchdogHeartbeat.js +116 -0
- package/dist/orchestrator/workflow/ConditionParser.js +232 -0
- package/dist/orchestrator/workflow/WorkflowEngine.js +379 -0
- package/dist/orchestrator/workflow/WorkflowParser.js +368 -0
- package/dist/orchestrator/workflow/WorkflowSchema.js +65 -0
- package/dist/orchestrator/workflow/WorkflowState.js +11 -0
- package/dist/reversa/ReversaAgent.js +134 -0
- package/dist/reversa/ReversaContracts.js +62 -0
- package/dist/reversa/ReversaExecutors.js +65 -0
- package/dist/skills/SkillRegistry.js +71 -0
- package/dist/squads/SquadManager.js +87 -0
- package/dist/test_admin_teams_networks.js +54 -0
- package/dist/test_agent_team_skill_network.js +15 -0
- package/dist/test_aiobuilder_cli_parity.js +169 -0
- package/dist/test_ask_exit.js +73 -0
- package/dist/test_atomic_writer.js +209 -0
- package/dist/test_autonomous_soak.js +141 -0
- package/dist/test_benchmark_engine.js +41 -0
- package/dist/test_brain_error_diagnostics.js +51 -0
- package/dist/test_brain_fallback_chain.js +93 -0
- package/dist/test_capability_genesis_engine.js +225 -0
- package/dist/test_capability_pack_schema.js +214 -0
- package/dist/test_catalog_quality.js +150 -0
- package/dist/test_cli_crud_roundtrip.js +154 -0
- package/dist/test_cli_diagnostics.js +131 -0
- package/dist/test_cli_doc_parity.js +126 -0
- package/dist/test_cli_help_surface.js +106 -0
- package/dist/test_cli_service_commands.js +83 -0
- package/dist/test_consequence_forecast_brain.js +165 -0
- package/dist/test_consequence_forecaster.js +24 -0
- package/dist/test_conversation_memory.js +36 -0
- package/dist/test_create_entities.js +54 -0
- package/dist/test_creator_placeholders_completed.js +177 -0
- package/dist/test_cron_manager.js +123 -0
- package/dist/test_daemon_sigterm.js +72 -0
- package/dist/test_deep_research_capability.js +87 -0
- package/dist/test_designmd_import_registry.js +16 -0
- package/dist/test_designmd_mode.js +50 -0
- package/dist/test_designmd_mode_workspace.js +13 -0
- package/dist/test_dist_templates_layout.js +135 -0
- package/dist/test_distributed_lock.js +201 -0
- package/dist/test_distribution_installability.js +67 -0
- package/dist/test_doctor_sandbox_check.js +44 -0
- package/dist/test_dream_organizer.js +25 -0
- package/dist/test_dual_mode.js +15 -0
- package/dist/test_enterprise_agentic_core.js +128 -0
- package/dist/test_forecast_brain_wiring.js +87 -0
- package/dist/test_gateway_telegram_guardrails.js +52 -0
- package/dist/test_governance.js +34 -0
- package/dist/test_governance_advanced.js +75 -0
- package/dist/test_governance_scope_ledger.js +147 -0
- package/dist/test_governance_v13_policies.js +44 -0
- package/dist/test_guided_creator_cli.js +100 -0
- package/dist/test_host_install_e2e.js +324 -0
- package/dist/test_host_installer.js +259 -0
- package/dist/test_host_installers_gemini_codex.js +95 -0
- package/dist/test_host_uninstaller.js +295 -0
- package/dist/test_install_flow.js +70 -0
- package/dist/test_install_flow_host_validation.js +143 -0
- package/dist/test_install_wizard.js +272 -0
- package/dist/test_integration_gemini_live.js +95 -0
- package/dist/test_integration_http_trigger_live.js +154 -0
- package/dist/test_integration_telegram_live.js +102 -0
- package/dist/test_job_lifecycle.js +16 -0
- package/dist/test_memory_orchestrator.js +33 -0
- package/dist/test_memory_promotion.js +36 -0
- package/dist/test_memory_retention.js +37 -0
- package/dist/test_mission_checkpoint.js +204 -0
- package/dist/test_multi_host_docs_parity.js +125 -0
- package/dist/test_openlife_auto_creator_routing.js +69 -0
- package/dist/test_openlife_evolution_surface.js +77 -0
- package/dist/test_openlife_gatekeeper_routing.js +15 -0
- package/dist/test_openlife_routing_surface.js +27 -0
- package/dist/test_openlife_runtime_source_truth.js +25 -0
- package/dist/test_operating_system.js +45 -0
- package/dist/test_optimization_loop.js +38 -0
- package/dist/test_orchestration_assets_lifecycle.js +78 -0
- package/dist/test_outcome_simulator.js +38 -0
- package/dist/test_performance_latency.js +215 -0
- package/dist/test_performance_scorecard.js +38 -0
- package/dist/test_phase1_check_exit.js +103 -0
- package/dist/test_phase6_board.js +31 -0
- package/dist/test_phase6_cadence.js +29 -0
- package/dist/test_phase6_ops.js +37 -0
- package/dist/test_post_mission_evaluation.js +190 -0
- package/dist/test_process_sandbox.js +88 -0
- package/dist/test_profile_toolset_mcp.js +125 -0
- package/dist/test_queue_scheduler.js +239 -0
- package/dist/test_release_gate.js +23 -0
- package/dist/test_remote_publish.js +193 -0
- package/dist/test_reversa_contracts_e2e.js +48 -0
- package/dist/test_reversa_export_and_strict.js +51 -0
- package/dist/test_reversa_full_execution.js +12 -0
- package/dist/test_reversa_lite.js +9 -0
- package/dist/test_royal_stack_golden.js +179 -0
- package/dist/test_runtime_health_backoff.js +154 -0
- package/dist/test_runtime_policy.js +26 -0
- package/dist/test_runtime_probe.js +19 -0
- package/dist/test_runtime_profile_oauth_only.js +262 -0
- package/dist/test_runtime_registry.js +11 -0
- package/dist/test_security_download_and_scan.js +103 -0
- package/dist/test_security_download_guard.js +14 -0
- package/dist/test_service_command_surface.js +12 -0
- package/dist/test_service_completion_policy.js +32 -0
- package/dist/test_service_guardrails_delete.js +12 -0
- package/dist/test_service_mode_explicit_only.js +174 -0
- package/dist/test_sources_import_ref.js +46 -0
- package/dist/test_sources_scaffold.js +43 -0
- package/dist/test_squad_skill_creator.js +305 -0
- package/dist/test_squad_skill_design_llm.js +176 -0
- package/dist/test_subsystems_org_state.js +271 -0
- package/dist/test_subsystems_promotion_memory_assets.js +343 -0
- package/dist/test_subsystems_routing_governance.js +234 -0
- package/dist/test_task_executor_sandbox_optin.js +127 -0
- package/dist/test_teammate_learning.js +15 -0
- package/dist/test_telegram_delete_guardrail.js +21 -0
- package/dist/test_toolset_enforcement.js +188 -0
- package/dist/test_trigger_basic_auth.js +112 -0
- package/dist/test_util/doc_parity.js +120 -0
- package/dist/test_v15_e2e_integration.js +207 -0
- package/dist/test_watchdog_heartbeat.js +152 -0
- package/dist/test_workflow_condition_parser.js +63 -0
- package/dist/test_workflow_e2e.js +240 -0
- package/dist/test_workflow_engine.js +330 -0
- package/dist/test_workflow_parser.js +245 -0
- package/dist/test_workflow_schema_backward_compat.js +197 -0
- package/dist-templates/README.md +91 -0
- package/dist-templates/claude-code/agents/openlife-atlas.md +52 -0
- package/dist-templates/claude-code/agents/openlife-forge.md +42 -0
- package/dist-templates/claude-code/agents/openlife-genesis.md +59 -0
- package/dist-templates/claude-code/agents/openlife-lyra.md +40 -0
- package/dist-templates/claude-code/agents/openlife-maestro.md +45 -0
- package/dist-templates/claude-code/commands/openlife/ask.md +14 -0
- package/dist-templates/claude-code/commands/openlife/doctor.md +19 -0
- package/dist-templates/claude-code/commands/openlife/dream.md +20 -0
- package/dist-templates/claude-code/commands/openlife/status.md +14 -0
- package/dist-templates/claude-code/mcp/openlife-orchestrator.json +46 -0
- package/dist-templates/codex/README.md +7 -0
- package/dist-templates/codex/agents/openlife-atlas.md +52 -0
- package/dist-templates/codex/agents/openlife-forge.md +42 -0
- package/dist-templates/codex/agents/openlife-genesis.md +59 -0
- package/dist-templates/codex/agents/openlife-lyra.md +40 -0
- package/dist-templates/codex/agents/openlife-maestro.md +45 -0
- package/dist-templates/codex/commands/openlife/ask.md +14 -0
- package/dist-templates/codex/commands/openlife/doctor.md +19 -0
- package/dist-templates/codex/commands/openlife/dream.md +20 -0
- package/dist-templates/codex/commands/openlife/status.md +14 -0
- package/dist-templates/codex/mcp/openlife-orchestrator.json +46 -0
- package/dist-templates/gemini-cli/README.md +8 -0
- package/dist-templates/gemini-cli/agents/openlife-atlas.md +52 -0
- package/dist-templates/gemini-cli/agents/openlife-forge.md +42 -0
- package/dist-templates/gemini-cli/agents/openlife-genesis.md +59 -0
- package/dist-templates/gemini-cli/agents/openlife-lyra.md +40 -0
- package/dist-templates/gemini-cli/agents/openlife-maestro.md +45 -0
- package/dist-templates/gemini-cli/commands/openlife/ask.md +14 -0
- package/dist-templates/gemini-cli/commands/openlife/doctor.md +19 -0
- package/dist-templates/gemini-cli/commands/openlife/dream.md +20 -0
- package/dist-templates/gemini-cli/commands/openlife/status.md +14 -0
- package/dist-templates/gemini-cli/mcp/openlife-orchestrator.json +46 -0
- package/dist-templates/skill-template/README.md +34 -0
- package/dist-templates/skill-template/SKILL.md.template +59 -0
- package/dist-templates/squad-template/README.md +82 -0
- package/dist-templates/squad-template/SQUAD.md.template +51 -0
- package/dist-templates/squad-template/agent-template.md +51 -0
- package/dist-templates/squad-template/checklist-template.md +25 -0
- package/dist-templates/squad-template/task-template.md +36 -0
- package/dist-templates/workflows/PORTED_WORKFLOWS.md +60 -0
- package/dist-templates/workflows/brownfield-discovery.yaml +137 -0
- package/dist-templates/workflows/greenfield-fullstack.yaml +132 -0
- package/dist-templates/workflows/qa-loop.yaml +125 -0
- package/dist-templates/workflows/story-development-cycle.yaml +80 -0
- package/docs/CHANGELOG_FEATURE_ROLLOUT_DESIGNMD.md +43 -0
- package/docs/EXTERNAL_SOURCES_AND_SECURITY_GUARD.md +33 -0
- package/docs/OPENLIFE_AUDIT_2026-05-06.md +170 -0
- package/docs/OPENLIFE_CONSOLIDATED_PLAN_2026-05-06.md +299 -0
- package/docs/OPENLIFE_DUAL_MODE_IMPLEMENTATION_PLAN.md +205 -0
- package/docs/OPENLIFE_EVOLUTION_SURFACE_2026-05-07.md +53 -0
- package/docs/OPENLIFE_SKILLS_IMPORT_2026-05-07.json +223 -0
- package/docs/OPENLIFE_SQUADS_IMPORT_2026-05-07.json +184 -0
- package/docs/PAPERCLIP_OPENLIFE_INVESTIGATION.md +85 -0
- package/docs/README.md +28 -0
- package/docs/RELEASE_ORGANIZATION_PLAN.md +164 -0
- package/docs/audit/CLI-EXECUTION-RESULTS.md +113 -0
- package/docs/audit/CLI-MATRIX.md +556 -0
- package/docs/audit/DOC-PARITY-GAPS.md +351 -0
- package/docs/audit/ORCHESTRATOR-MATRIX.md +136 -0
- package/docs/audit/TEST-COVERAGE-GAPS.md +334 -0
- package/docs/audit/integrations/SKIPPED.md +101 -0
- package/docs/autonomous-install.md +79 -0
- package/docs/capability-genesis.md +137 -0
- package/docs/capability-pack-schema.md +157 -0
- package/docs/commands.md +82 -0
- package/docs/deep-research-capability.md +114 -0
- package/docs/development/typescript-conventions.md +95 -0
- package/docs/host-installers.md +68 -0
- package/docs/install/aiobuilder.md +70 -0
- package/docs/install/claude-code.md +83 -0
- package/docs/install/codex.md +64 -0
- package/docs/install/gemini-cli.md +64 -0
- package/docs/install/runtime-profiles.md +83 -0
- package/docs/openlife-agent-os-blueprint.md +114 -0
- package/docs/openlife-install-backlog.md +115 -0
- package/docs/openlife-install-spec.md +306 -0
- package/docs/operations/CLOUD_CUTOVER_AUDIT.md +37 -0
- package/docs/operations/PHASE_PROGRESS_CONTINUATION.md +24 -0
- package/docs/performance-benchmarks.md +83 -0
- package/docs/planning/v1.3-capability-genesis.md +157 -0
- package/docs/plans/2026-05-05-admin-interface-professional-dark-premium-plan.md +84 -0
- package/docs/plans/2026-05-05-openlife-autonomous-domain-marketplace-masterplan.md +122 -0
- package/docs/quickstart.md +60 -0
- package/docs/release-process.md +236 -0
- package/docs/roadmap/OPENLIFE_MASTER_PLAN_CLOUD_V3.md +97 -0
- package/docs/sandboxing-research.md +117 -0
- package/docs/stories/epic-feature-audit/1.1.story.md +84 -0
- package/docs/stories/epic-feature-audit/1.2.story.md +102 -0
- package/docs/stories/epic-feature-audit/1.3.story.md +93 -0
- package/docs/stories/epic-feature-audit/1.5.story.md +121 -0
- package/docs/stories/epic-feature-audit/1.6.story.md +80 -0
- package/docs/stories/epic-feature-completeness/2.1.story.md +70 -0
- package/docs/stories/epic-feature-completeness/2.2.story.md +49 -0
- package/docs/stories/epic-feature-completeness/2.3.story.md +74 -0
- package/docs/stories/epic-feature-completeness/2.4.story.md +71 -0
- package/docs/stories/epic-feature-completeness/3.1.story.md +56 -0
- package/docs/stories/epic-feature-completeness/3.2.story.md +80 -0
- package/docs/stories/epic-feature-completeness/3.3.story.md +68 -0
- package/docs/stories/epic-feature-completeness/3.4.story.md +71 -0
- package/docs/stories/epic-feature-completeness/3.5.story.md +72 -0
- package/docs/stories/epic-feature-completeness/3.6.story.md +69 -0
- package/docs/stories/epic-feature-completeness/3.7.story.md +68 -0
- package/docs/stories/epic-feature-completeness/3.8.story.md +57 -0
- package/docs/toolset-enforcement.md +122 -0
- package/docs/v1.4-changelog.md +159 -0
- package/docs/v1.5-changelog.md +106 -0
- package/docs/v1.5-roadmap.md +121 -0
- package/docs/v1.6-changelog.md +67 -0
- package/docs/v1.6-roadmap.md +89 -0
- package/docs/v1.7-changelog.md +98 -0
- package/docs/workflow-schema.md +177 -0
- package/package.json +177 -0
- package/scripts/clean-test-pollution.js +61 -0
- package/scripts/openlife-agent-start.sh +6 -0
- package/scripts/openlife-agent.service.example +13 -0
- package/scripts/openlife-agent.supervisord.conf.example +8 -0
- package/scripts/openlife-autonomous-install.sh +29 -0
- package/scripts/postinstall-check.sh +37 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* test_consequence_forecast_brain.ts — Story 14.3 (v1.5)
|
|
4
|
+
*
|
|
5
|
+
* Asserts:
|
|
6
|
+
* 1. forecastWithBrain() without any provider key returns the base
|
|
7
|
+
* heuristic forecast with no `brain` field attached.
|
|
8
|
+
* 2. With a stubbed Brain that returns valid JSON, the enrichment is
|
|
9
|
+
* attached with source='brain' and persisted to the cache file.
|
|
10
|
+
* 3. A second call for the same (goal, governanceRisk) reads from cache
|
|
11
|
+
* with source='cache' and does NOT invoke Brain again.
|
|
12
|
+
* 4. OPENLIFE_FORECAST_CACHE=off short-circuits both read and write.
|
|
13
|
+
* 5. Garbage Brain output yields no enrichment (base forecast returned).
|
|
14
|
+
* 6. OPENLIFE_FORECAST_CACHE_TTL_HOURS=0 forces a re-fetch on each call.
|
|
15
|
+
*/
|
|
16
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(o, k2, desc);
|
|
23
|
+
}) : (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
o[k2] = m[k];
|
|
26
|
+
}));
|
|
27
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
+
}) : function(o, v) {
|
|
30
|
+
o["default"] = v;
|
|
31
|
+
});
|
|
32
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
+
var ownKeys = function(o) {
|
|
34
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
+
var ar = [];
|
|
36
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
+
return ar;
|
|
38
|
+
};
|
|
39
|
+
return ownKeys(o);
|
|
40
|
+
};
|
|
41
|
+
return function (mod) {
|
|
42
|
+
if (mod && mod.__esModule) return mod;
|
|
43
|
+
var result = {};
|
|
44
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
+
__setModuleDefault(result, mod);
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
})();
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
const os = __importStar(require("os"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const crypto = __importStar(require("crypto"));
|
|
54
|
+
function assertTrue(cond, label) {
|
|
55
|
+
if (!cond)
|
|
56
|
+
throw new Error(`ASSERT_FAILED[${label}]`);
|
|
57
|
+
}
|
|
58
|
+
function installBrainMock(mock) {
|
|
59
|
+
const brainPath = require.resolve('./orchestrator/Brain');
|
|
60
|
+
const orig = require.cache[brainPath];
|
|
61
|
+
require.cache[brainPath] = {
|
|
62
|
+
...orig,
|
|
63
|
+
exports: {
|
|
64
|
+
Brain: class {
|
|
65
|
+
isAnyProviderAvailable() { return mock.available; }
|
|
66
|
+
async think() { mock.invocations += 1; return mock.reply; }
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
return () => {
|
|
71
|
+
if (orig)
|
|
72
|
+
require.cache[brainPath] = orig;
|
|
73
|
+
else
|
|
74
|
+
delete require.cache[brainPath];
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async function main() {
|
|
78
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'consequence-brain-'));
|
|
79
|
+
process.env.OPENLIFE_STATE_DIR = tmp;
|
|
80
|
+
try {
|
|
81
|
+
const { ConsequenceForecaster } = require('./orchestrator/ConsequenceForecaster');
|
|
82
|
+
const fc = new ConsequenceForecaster();
|
|
83
|
+
const basePolicy = { executionMode: 'balanced', swarmMode: 'none', maxBranches: 1, rationale: 'base' };
|
|
84
|
+
// Scenario 1: no Brain → no enrichment
|
|
85
|
+
const restoreNoBrain = installBrainMock({ invocations: 0, reply: '', available: false });
|
|
86
|
+
try {
|
|
87
|
+
const r1 = await fc.forecastWithBrain('refactor auth flow', 'low', basePolicy, ['codex']);
|
|
88
|
+
assertTrue(r1.selectedPath && r1.selectedPath.id, '[14.3] base forecast present');
|
|
89
|
+
assertTrue(r1.brain === undefined, '[14.3] no Brain key → no enrichment');
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
restoreNoBrain();
|
|
93
|
+
}
|
|
94
|
+
console.log('[14.3] no Brain key → pure heuristic ✓');
|
|
95
|
+
// Scenario 2 + 3: stubbed Brain → enriched, then cache hit
|
|
96
|
+
const ok = { invocations: 0, reply: '{"riskAmplifiers":["leaks logs"],"opportunityFactors":["enables audit"],"overallVerdict":"caution","rationale":"reasonable"}', available: true };
|
|
97
|
+
const restoreOk = installBrainMock(ok);
|
|
98
|
+
try {
|
|
99
|
+
const r2 = await fc.forecastWithBrain('audit the access ledger', 'medium', basePolicy, ['codex']);
|
|
100
|
+
assertTrue(r2.brain && r2.brain.source === 'brain', '[14.3] brain enrichment attached, source=brain');
|
|
101
|
+
assertTrue(r2.brain.overallVerdict === 'caution', '[14.3] verdict landed');
|
|
102
|
+
assertTrue(r2.brain.riskAmplifiers[0] === 'leaks logs', '[14.3] riskAmplifiers landed');
|
|
103
|
+
// Cache file exists
|
|
104
|
+
const goalHash = crypto.createHash('sha256').update('audit the access ledger').digest('hex');
|
|
105
|
+
const cacheFile = path.join(tmp, 'forecasts', `${goalHash}_medium.json`);
|
|
106
|
+
assertTrue(fs.existsSync(cacheFile), '[14.3] cache file persisted');
|
|
107
|
+
// Second call → cache hit
|
|
108
|
+
const r3 = await fc.forecastWithBrain('audit the access ledger', 'medium', basePolicy, ['codex']);
|
|
109
|
+
assertTrue(r3.brain && r3.brain.source === 'cache', `[14.3] second call hits cache (got ${r3.brain?.source})`);
|
|
110
|
+
assertTrue(ok.invocations === 1, `[14.3] Brain not re-invoked on cache hit (got ${ok.invocations} invocations)`);
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
restoreOk();
|
|
114
|
+
}
|
|
115
|
+
console.log('[14.3] brain enrichment + cache hit ✓');
|
|
116
|
+
// Scenario 4: cache OFF → always recompute
|
|
117
|
+
process.env.OPENLIFE_FORECAST_CACHE = 'off';
|
|
118
|
+
const off = { invocations: 0, reply: '{"riskAmplifiers":["x"],"opportunityFactors":["y"],"overallVerdict":"safe","rationale":"ok"}', available: true };
|
|
119
|
+
const restoreOff = installBrainMock(off);
|
|
120
|
+
try {
|
|
121
|
+
await fc.forecastWithBrain('audit the access ledger', 'medium', basePolicy, ['codex']);
|
|
122
|
+
await fc.forecastWithBrain('audit the access ledger', 'medium', basePolicy, ['codex']);
|
|
123
|
+
assertTrue(off.invocations === 2, `[14.3] cache OFF → both calls hit Brain (got ${off.invocations})`);
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
restoreOff();
|
|
127
|
+
delete process.env.OPENLIFE_FORECAST_CACHE;
|
|
128
|
+
}
|
|
129
|
+
console.log('[14.3] OPENLIFE_FORECAST_CACHE=off short-circuits cache ✓');
|
|
130
|
+
// Scenario 5: garbage Brain output → no enrichment
|
|
131
|
+
const garbage = { invocations: 0, reply: 'not valid json', available: true };
|
|
132
|
+
const restoreGarbage = installBrainMock(garbage);
|
|
133
|
+
try {
|
|
134
|
+
const r5 = await fc.forecastWithBrain('garbage-goal-' + Date.now(), 'low', basePolicy, ['codex']);
|
|
135
|
+
assertTrue(r5.brain === undefined, '[14.3] garbage output → no enrichment attached');
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
restoreGarbage();
|
|
139
|
+
}
|
|
140
|
+
console.log('[14.3] garbage Brain output → no enrichment ✓');
|
|
141
|
+
// Scenario 6: TTL=0 → cache stale immediately, refetch
|
|
142
|
+
process.env.OPENLIFE_FORECAST_CACHE_TTL_HOURS = '0';
|
|
143
|
+
const ttl = { invocations: 0, reply: '{"riskAmplifiers":["a"],"opportunityFactors":["b"],"overallVerdict":"safe","rationale":""}', available: true };
|
|
144
|
+
const restoreTtl = installBrainMock(ttl);
|
|
145
|
+
try {
|
|
146
|
+
await fc.forecastWithBrain('ttl-goal-' + Date.now(), 'low', basePolicy, ['codex']);
|
|
147
|
+
await fc.forecastWithBrain('ttl-goal-' + Date.now(), 'low', basePolicy, ['codex']); // different goal → fresh anyway
|
|
148
|
+
assertTrue(ttl.invocations === 2, '[14.3] each unique goal hits Brain');
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
restoreTtl();
|
|
152
|
+
delete process.env.OPENLIFE_FORECAST_CACHE_TTL_HOURS;
|
|
153
|
+
}
|
|
154
|
+
console.log('[14.3] TTL boundary respected ✓');
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
delete process.env.OPENLIFE_STATE_DIR;
|
|
158
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
159
|
+
}
|
|
160
|
+
console.log('TEST_CONSEQUENCE_FORECAST_BRAIN_OK');
|
|
161
|
+
}
|
|
162
|
+
main().catch((err) => {
|
|
163
|
+
console.error('[consequence-forecast-brain] FAILED:', err instanceof Error ? err.message : err);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ConsequenceForecaster_1 = require("./orchestrator/ConsequenceForecaster");
|
|
4
|
+
function assert(condition, message) {
|
|
5
|
+
if (!condition)
|
|
6
|
+
throw new Error(message);
|
|
7
|
+
}
|
|
8
|
+
async function main() {
|
|
9
|
+
const f = new ConsequenceForecaster_1.ConsequenceForecaster();
|
|
10
|
+
const high = f.forecast('deploy prod', 'high', { executionMode: 'performance', swarmMode: 'parallel', maxBranches: 3, rationale: 'x' }, ['codex', 'claude']);
|
|
11
|
+
assert(high.selectedPath.id === 'conservative', 'high risk should choose conservative path');
|
|
12
|
+
const lowSafe = f.forecast('deploy produção com compliance', 'low', { executionMode: 'balanced', swarmMode: 'light', maxBranches: 2, rationale: 'x' }, ['codex', 'claude']);
|
|
13
|
+
assert(['conservative', 'balanced'].includes(lowSafe.selectedPath.id), 'safety-biased low risk should avoid aggressive path');
|
|
14
|
+
assert(lowSafe.selectedPath.id !== 'aggressive', 'safety-biased low risk should avoid aggressive path');
|
|
15
|
+
const lowSpeed = f.forecast('hotfix urgente', 'low', { executionMode: 'balanced', swarmMode: 'light', maxBranches: 2, rationale: 'x' }, ['codex', 'claude']);
|
|
16
|
+
assert(lowSpeed.selectedPath.id !== 'conservative', 'speed-biased low risk should avoid conservative path when possible');
|
|
17
|
+
assert(lowSpeed.consideredPaths.length === 3, 'should consider 3 paths');
|
|
18
|
+
assert(lowSpeed.executiveSummary.includes('Viés do objetivo='), 'summary should include goal bias');
|
|
19
|
+
console.log('TEST_CONSEQUENCE_FORECASTER_OK');
|
|
20
|
+
}
|
|
21
|
+
main().catch((error) => {
|
|
22
|
+
console.error(error.message || error);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ConversationMemory_1 = require("./memory/ConversationMemory");
|
|
4
|
+
function assert(condition, message) {
|
|
5
|
+
if (!condition)
|
|
6
|
+
throw new Error(message);
|
|
7
|
+
}
|
|
8
|
+
async function main() {
|
|
9
|
+
const fakeMemory = {
|
|
10
|
+
async search(query) {
|
|
11
|
+
return `hit:${query}`;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const fakeSession = {
|
|
15
|
+
getSession(userId) {
|
|
16
|
+
return {
|
|
17
|
+
id: userId,
|
|
18
|
+
lastActive: new Date().toISOString(),
|
|
19
|
+
history: [
|
|
20
|
+
{ role: 'user', content: 'quero evoluir a memória', timestamp: new Date().toISOString() },
|
|
21
|
+
{ role: 'agent', content: 'vamos melhorar recuperação', timestamp: new Date().toISOString() }
|
|
22
|
+
]
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const cm = new ConversationMemory_1.ConversationMemory(fakeMemory, fakeSession);
|
|
27
|
+
const context = await cm.recoverContext('rafa', 'memory retrieval');
|
|
28
|
+
assert(context.memorySnippet.includes('hit:memory retrieval'), 'should include memory search result');
|
|
29
|
+
assert(context.recentHistory.includes('quero evoluir a memória'), 'should include recent history');
|
|
30
|
+
assert(context.autoRecovered === true, 'should mark context as recovered');
|
|
31
|
+
console.log('TEST_CONVERSATION_MEMORY_OK');
|
|
32
|
+
}
|
|
33
|
+
main().catch((error) => {
|
|
34
|
+
console.error(error.message || error);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const child_process_1 = require("child_process");
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
(0, child_process_1.execSync)('node dist/index.js agents create test-agent --role specialist --notes "ok"', { stdio: 'pipe' });
|
|
40
|
+
(0, child_process_1.execSync)('node dist/index.js squads create test-squad --domain growth --notes "ok"', { stdio: 'pipe' });
|
|
41
|
+
(0, child_process_1.execSync)('node dist/index.js skills create test-skill-autogen --notes "ok"', { stdio: 'pipe' });
|
|
42
|
+
(0, child_process_1.execSync)('node dist/index.js mcp create test-mcp --transport stdio --entry "npx -y test"', { stdio: 'pipe' });
|
|
43
|
+
const checks = [
|
|
44
|
+
'.catalog/agents/test-agent/AGENT.md',
|
|
45
|
+
'.catalog/squads/test-squad/SQUAD.md',
|
|
46
|
+
'.catalog/skills/test-skill-autogen/SKILL.md',
|
|
47
|
+
'.catalog/mcps/test-mcp/mcp.json'
|
|
48
|
+
];
|
|
49
|
+
for (const p of checks) {
|
|
50
|
+
const full = path.join(process.cwd(), p);
|
|
51
|
+
if (!fs.existsSync(full))
|
|
52
|
+
throw new Error(`missing: ${p}`);
|
|
53
|
+
}
|
|
54
|
+
console.log('TEST_CREATE_ENTITIES_OK');
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* test_creator_placeholders_completed.ts — Stories 11.1 – 11.6
|
|
4
|
+
*
|
|
5
|
+
* Asserts that SquadCreator + SkillCreator no longer ship placeholders.
|
|
6
|
+
* For each of migrate / extend / publish we round-trip on a tmp catalog.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const os = __importStar(require("os"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const crypto = __importStar(require("crypto"));
|
|
46
|
+
const SquadCreator_1 = require("./orchestrator/SquadCreator");
|
|
47
|
+
const SkillCreator_1 = require("./orchestrator/SkillCreator");
|
|
48
|
+
const SQUAD_TEMPLATE = path.resolve(__dirname, '..', 'dist-templates', 'squad-template');
|
|
49
|
+
const SKILL_TEMPLATE = path.resolve(__dirname, '..', 'dist-templates', 'skill-template');
|
|
50
|
+
function assertTrue(cond, label) {
|
|
51
|
+
if (!cond)
|
|
52
|
+
throw new Error(`ASSERT_FAILED[${label}]`);
|
|
53
|
+
}
|
|
54
|
+
function mkTmp() {
|
|
55
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), 'creator-real-'));
|
|
56
|
+
}
|
|
57
|
+
function rmTmp(dir) {
|
|
58
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
function runSquadRoundTrip() {
|
|
61
|
+
const tmp = mkTmp();
|
|
62
|
+
process.env.OPENLIFE_STATE_DIR = path.join(tmp, '.openlife');
|
|
63
|
+
try {
|
|
64
|
+
const sc = new SquadCreator_1.SquadCreator({
|
|
65
|
+
catalogRoot: path.join(tmp, '.catalog', 'squads'),
|
|
66
|
+
templateRoot: SQUAD_TEMPLATE,
|
|
67
|
+
});
|
|
68
|
+
// Seed: create a squad with version 0.1.0.
|
|
69
|
+
const proposal = {
|
|
70
|
+
id: 'demo-squad',
|
|
71
|
+
name: 'Demo Squad',
|
|
72
|
+
description: 'demo',
|
|
73
|
+
version: '0.1.0',
|
|
74
|
+
status: 'draft',
|
|
75
|
+
agents: [{ id: 'lead', name: 'Lead', role: 'lead' }],
|
|
76
|
+
};
|
|
77
|
+
const created = sc.create(proposal);
|
|
78
|
+
assertTrue(created.ok, '[11.x] seed squad created');
|
|
79
|
+
// ── 11.1 migrate ─────────────────────────────────────────────
|
|
80
|
+
const wrongFrom = sc.migrate('demo-squad', '9.9.9', '1.0.0');
|
|
81
|
+
assertTrue(!wrongFrom.ok && wrongFrom.error === 'version_mismatch', '[11.1] version_mismatch detected');
|
|
82
|
+
const migrated = sc.migrate('demo-squad', '0.1.0', '1.0.0');
|
|
83
|
+
assertTrue(migrated.ok, '[11.1] migrate ok');
|
|
84
|
+
const fmAfter = fs.readFileSync(path.join(tmp, '.catalog', 'squads', 'demo-squad', 'SQUAD.md'), 'utf-8');
|
|
85
|
+
assertTrue(/^version:\s*"?1\.0\.0"?$/m.test(fmAfter), '[11.1] frontmatter version bumped');
|
|
86
|
+
console.log('[11.1] SquadCreator.migrate atomic version bump ✓');
|
|
87
|
+
// ── 11.2 extend (agent + task + workflow + checklist) ────────
|
|
88
|
+
const ex1 = sc.extend('demo-squad', { kind: 'agent', spec: { id: 'qa', name: 'QA', role: 'reviewer' } });
|
|
89
|
+
assertTrue(ex1.ok && (ex1.filesAdded || []).length === 1, '[11.2] extend agent ok');
|
|
90
|
+
assertTrue(fs.existsSync(path.join(tmp, '.catalog', 'squads', 'demo-squad', 'agents', 'qa.md')), '[11.2] agent file written');
|
|
91
|
+
const ex1dup = sc.extend('demo-squad', { kind: 'agent', spec: { id: 'qa', name: 'QA', role: 'reviewer' } });
|
|
92
|
+
assertTrue(!ex1dup.ok && ex1dup.error === 'component_exists', '[11.2] re-add agent rejected');
|
|
93
|
+
const ex2 = sc.extend('demo-squad', { kind: 'task', spec: { id: 'review-pr', ownerAgentId: 'qa', purpose: 'gate review' } });
|
|
94
|
+
assertTrue(ex2.ok, '[11.2] extend task ok');
|
|
95
|
+
const ex3 = sc.extend('demo-squad', { kind: 'workflow', id: 'demo-wf', content: 'id: demo-wf\nname: Demo\nsteps: []\n' });
|
|
96
|
+
assertTrue(ex3.ok, '[11.2] extend workflow ok');
|
|
97
|
+
const ex4 = sc.extend('demo-squad', { kind: 'checklist', spec: { id: 'gate', purpose: 'final', items: ['ok'] } });
|
|
98
|
+
assertTrue(ex4.ok, '[11.2] extend checklist ok');
|
|
99
|
+
// Extensions log was appended
|
|
100
|
+
const squadMd = fs.readFileSync(path.join(tmp, '.catalog', 'squads', 'demo-squad', 'SQUAD.md'), 'utf-8');
|
|
101
|
+
assertTrue(/## Extensions/.test(squadMd), '[11.2] Extensions log section present');
|
|
102
|
+
console.log('[11.2] SquadCreator.extend agent/task/workflow/checklist ✓');
|
|
103
|
+
// ── 11.3 publish — sha + ledger append + status flip ─────────
|
|
104
|
+
const pub = sc.publish('demo-squad');
|
|
105
|
+
assertTrue(pub.ok, '[11.3] publish ok');
|
|
106
|
+
assertTrue(!!pub.sha256 && pub.sha256.length === 64, '[11.3] sha256 length 64 hex');
|
|
107
|
+
const finalContent = fs.readFileSync(path.join(tmp, '.catalog', 'squads', 'demo-squad', 'SQUAD.md'), 'utf-8');
|
|
108
|
+
const expected = crypto.createHash('sha256').update(finalContent).digest('hex');
|
|
109
|
+
assertTrue(pub.sha256 === expected, '[11.3] sha256 matches file');
|
|
110
|
+
const ledger = fs.readFileSync(path.join(tmp, '.openlife', 'published-assets.jsonl'), 'utf-8');
|
|
111
|
+
assertTrue(ledger.includes('"id":"demo-squad"'), '[11.3] ledger entry written');
|
|
112
|
+
assertTrue(/^status:\s*active$/m.test(finalContent), '[11.3] status flipped to active');
|
|
113
|
+
console.log('[11.3] SquadCreator.publish sha+ledger+status ✓');
|
|
114
|
+
// Error path: publish on non-existent squad
|
|
115
|
+
const noSq = sc.publish('does-not-exist');
|
|
116
|
+
assertTrue(!noSq.ok && noSq.error === 'squad_not_found', '[11.3] publish squad_not_found');
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
rmTmp(tmp);
|
|
120
|
+
delete process.env.OPENLIFE_STATE_DIR;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function runSkillRoundTrip() {
|
|
124
|
+
const tmp = mkTmp();
|
|
125
|
+
process.env.OPENLIFE_STATE_DIR = path.join(tmp, '.openlife');
|
|
126
|
+
try {
|
|
127
|
+
const sk = new SkillCreator_1.SkillCreator({
|
|
128
|
+
catalogRoot: path.join(tmp, '.catalog', 'skills'),
|
|
129
|
+
templateRoot: SKILL_TEMPLATE,
|
|
130
|
+
});
|
|
131
|
+
const proposal = {
|
|
132
|
+
id: 'demo-skill',
|
|
133
|
+
name: 'Demo Skill',
|
|
134
|
+
description: 'demo',
|
|
135
|
+
version: '0.1.0',
|
|
136
|
+
status: 'draft',
|
|
137
|
+
whenToUse: ['testing'],
|
|
138
|
+
procedure: ['step a', 'step b'],
|
|
139
|
+
};
|
|
140
|
+
const created = sk.create(proposal);
|
|
141
|
+
assertTrue(created.ok, '[11.x] seed skill created');
|
|
142
|
+
// ── 11.4 migrate ─────────────────────────────────────────────
|
|
143
|
+
const mig = sk.migrate('demo-skill', '0.1.0', '1.0.0');
|
|
144
|
+
assertTrue(mig.ok, '[11.4] migrate ok');
|
|
145
|
+
const skMd1 = fs.readFileSync(path.join(tmp, '.catalog', 'skills', 'demo-skill', 'SKILL.md'), 'utf-8');
|
|
146
|
+
assertTrue(/^version:\s*1\.0\.0$/m.test(skMd1), '[11.4] frontmatter version bumped');
|
|
147
|
+
console.log('[11.4] SkillCreator.migrate atomic version bump ✓');
|
|
148
|
+
// ── 11.5 extend (whenToUse + procedure) ──────────────────────
|
|
149
|
+
const ex1 = sk.extend('demo-skill', { section: 'whenToUse', items: ['production debugging', 'incident review'] });
|
|
150
|
+
assertTrue(ex1.ok && ex1.itemsAdded === 2, '[11.5] extend whenToUse');
|
|
151
|
+
const skMd2 = fs.readFileSync(path.join(tmp, '.catalog', 'skills', 'demo-skill', 'SKILL.md'), 'utf-8');
|
|
152
|
+
assertTrue(skMd2.includes('- production debugging'), '[11.5] whenToUse bullet appended');
|
|
153
|
+
const ex2 = sk.extend('demo-skill', { section: 'procedure', items: ['validate output'] });
|
|
154
|
+
assertTrue(ex2.ok, '[11.5] extend procedure');
|
|
155
|
+
const skMd3 = fs.readFileSync(path.join(tmp, '.catalog', 'skills', 'demo-skill', 'SKILL.md'), 'utf-8');
|
|
156
|
+
// procedure had 2 numbered items in template (template renders STEP_1..STEP_4 — first two filled).
|
|
157
|
+
// After extend we expect a higher-numbered line for "validate output".
|
|
158
|
+
assertTrue(/\d+\.\s+validate output/.test(skMd3), '[11.5] procedure item appended with next number');
|
|
159
|
+
console.log('[11.5] SkillCreator.extend section append ✓');
|
|
160
|
+
// ── 11.6 publish ─────────────────────────────────────────────
|
|
161
|
+
const pub = sk.publish('demo-skill');
|
|
162
|
+
assertTrue(pub.ok, '[11.6] publish ok');
|
|
163
|
+
assertTrue(!!pub.sha256 && pub.sha256.length === 64, '[11.6] sha256 64 hex');
|
|
164
|
+
const ledger = fs.readFileSync(path.join(tmp, '.openlife', 'published-assets.jsonl'), 'utf-8');
|
|
165
|
+
assertTrue(ledger.includes('"id":"demo-skill"'), '[11.6] ledger entry written');
|
|
166
|
+
const finalContent = fs.readFileSync(path.join(tmp, '.catalog', 'skills', 'demo-skill', 'SKILL.md'), 'utf-8');
|
|
167
|
+
assertTrue(/^status:\s*active$/m.test(finalContent), '[11.6] status flipped to active');
|
|
168
|
+
console.log('[11.6] SkillCreator.publish sha+ledger+status ✓');
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
rmTmp(tmp);
|
|
172
|
+
delete process.env.OPENLIFE_STATE_DIR;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
runSquadRoundTrip();
|
|
176
|
+
runSkillRoundTrip();
|
|
177
|
+
console.log('TEST_CREATOR_PLACEHOLDERS_COMPLETED_OK');
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* test_cron_manager.ts — Story 5.1
|
|
4
|
+
*
|
|
5
|
+
* Asserts the CronManager + parser surface:
|
|
6
|
+
* 1. parseCronExpression validates / rejects expressions
|
|
7
|
+
* 2. CronManager.create/list/pause/resume/remove
|
|
8
|
+
* 3. tick() fires matching jobs and skips paused ones
|
|
9
|
+
* 4. tick() is idempotent within the same minute
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const os = __importStar(require("os"));
|
|
48
|
+
const CronManager_1 = require("./orchestrator/CronManager");
|
|
49
|
+
function assertTrue(cond, label) {
|
|
50
|
+
if (!cond)
|
|
51
|
+
throw new Error(`ASSERT_FAILED[${label}]`);
|
|
52
|
+
}
|
|
53
|
+
async function main() {
|
|
54
|
+
// ── Scenario 1: parser ───────────────────────────────────────────
|
|
55
|
+
assertTrue((0, CronManager_1.parseCronExpression)('* * * * *').ok, 'wildcard accepted');
|
|
56
|
+
assertTrue((0, CronManager_1.parseCronExpression)('0 9 * * 1-5').ok, '9am weekdays accepted');
|
|
57
|
+
assertTrue((0, CronManager_1.parseCronExpression)('*/15 * * * *').ok, 'every 15 min accepted');
|
|
58
|
+
assertTrue(!(0, CronManager_1.parseCronExpression)('').ok, 'empty rejected');
|
|
59
|
+
assertTrue(!(0, CronManager_1.parseCronExpression)('* * * *').ok, '4 fields rejected');
|
|
60
|
+
assertTrue(!(0, CronManager_1.parseCronExpression)('60 * * * *').ok, 'minute=60 rejected');
|
|
61
|
+
assertTrue(!(0, CronManager_1.parseCronExpression)('* 25 * * *').ok, 'hour=25 rejected');
|
|
62
|
+
console.log('[5.1] parser ok ✓');
|
|
63
|
+
// ── Scenario 2: matchesCron sanity ───────────────────────────────
|
|
64
|
+
const f1 = (0, CronManager_1.parseCronExpression)('* * * * *').fields;
|
|
65
|
+
assertTrue((0, CronManager_1.matchesCron)(f1, new Date()), 'wildcard matches now');
|
|
66
|
+
// ── Scenario 3: lifecycle CRUD ──────────────────────────────────
|
|
67
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'cron-mgr-'));
|
|
68
|
+
try {
|
|
69
|
+
const cron = new CronManager_1.CronManager({ stateDir: tmp });
|
|
70
|
+
const job = cron.create('job-1', '* * * * *', { mission: 'demo' });
|
|
71
|
+
assertTrue(job.id === 'job-1' && job.status === 'active', 'create returns active');
|
|
72
|
+
assertTrue(cron.list().length === 1, 'list shows 1 job');
|
|
73
|
+
assertTrue(cron.pause('job-1'), 'pause ok');
|
|
74
|
+
assertTrue(cron.list()[0].status === 'paused', 'job paused');
|
|
75
|
+
assertTrue(cron.resume('job-1'), 'resume ok');
|
|
76
|
+
assertTrue(cron.list()[0].status === 'active', 'job active again');
|
|
77
|
+
console.log('[5.1] CRUD ✓');
|
|
78
|
+
// ── Scenario 4: invalid id rejected ─────────────────────────────
|
|
79
|
+
let invalidIdRejected = false;
|
|
80
|
+
try {
|
|
81
|
+
cron.create('Bad ID with space', '* * * * *');
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
invalidIdRejected = e.code === 'invalid_cron_id';
|
|
85
|
+
}
|
|
86
|
+
assertTrue(invalidIdRejected, 'invalid_cron_id surfaced');
|
|
87
|
+
console.log('[5.1] invalid_cron_id ✓');
|
|
88
|
+
// ── Scenario 5: tick fires matching jobs ─────────────────────────
|
|
89
|
+
const fired = [];
|
|
90
|
+
const executor = {
|
|
91
|
+
execute(j) { fired.push(j.id); },
|
|
92
|
+
};
|
|
93
|
+
const cron2 = new CronManager_1.CronManager({ stateDir: tmp, executor });
|
|
94
|
+
const firedNow = await cron2.tick();
|
|
95
|
+
assertTrue(firedNow.length === 1, `tick fires 1 job (got ${firedNow.length})`);
|
|
96
|
+
assertTrue(fired.length === 1 && fired[0] === 'job-1', 'executor invoked');
|
|
97
|
+
console.log('[5.1] tick fires ✓');
|
|
98
|
+
// ── Scenario 6: tick is idempotent within the same minute ───────
|
|
99
|
+
const firedAgain = await cron2.tick();
|
|
100
|
+
assertTrue(firedAgain.length === 0, 'second tick same minute fires nothing');
|
|
101
|
+
console.log('[5.1] tick idempotent within minute ✓');
|
|
102
|
+
// ── Scenario 7: paused jobs do not fire on a future minute ──────
|
|
103
|
+
cron2.pause('job-1');
|
|
104
|
+
// Bump time by 1 minute via a Date subclass — simulate next-minute tick.
|
|
105
|
+
const future = new Date(Date.now() + 60 * 1000);
|
|
106
|
+
const firedFuture = await cron2.tick(future);
|
|
107
|
+
assertTrue(firedFuture.length === 0, 'paused job does not fire next minute');
|
|
108
|
+
console.log('[5.1] paused jobs do not fire ✓');
|
|
109
|
+
// ── Scenario 8: remove ──────────────────────────────────────────
|
|
110
|
+
assertTrue(cron.remove('job-1'), 'remove ok');
|
|
111
|
+
assertTrue(cron.list().length === 0, 'job removed');
|
|
112
|
+
assertTrue(!cron.remove('job-1'), 'remove returns false on missing');
|
|
113
|
+
console.log('[5.1] remove ✓');
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
117
|
+
}
|
|
118
|
+
console.log('TEST_CRON_MANAGER_OK');
|
|
119
|
+
}
|
|
120
|
+
main().catch((err) => {
|
|
121
|
+
console.error('[cron-manager] FAILED:', err instanceof Error ? err.message : err);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const http = __importStar(require("http"));
|
|
37
|
+
process.env.PORT = '3099';
|
|
38
|
+
process.env.TELEGRAM_BOT_TOKEN = '';
|
|
39
|
+
process.env.OPENLIFE_STATE_DIR = process.env.OPENLIFE_STATE_DIR || '.openlife';
|
|
40
|
+
const { Gateway } = require('./orchestrator/Gateway');
|
|
41
|
+
async function probe(port, timeoutMs = 1500) {
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
const req = http.get({ host: '127.0.0.1', port, path: '/', timeout: timeoutMs }, (res) => {
|
|
44
|
+
res.resume();
|
|
45
|
+
resolve((res.statusCode || 0) > 0);
|
|
46
|
+
});
|
|
47
|
+
req.on('error', () => resolve(false));
|
|
48
|
+
req.on('timeout', () => { req.destroy(); resolve(false); });
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async function main() {
|
|
52
|
+
const gateway = new Gateway();
|
|
53
|
+
gateway.start();
|
|
54
|
+
await new Promise((r) => setTimeout(r, 600));
|
|
55
|
+
const upBefore = await probe(3099);
|
|
56
|
+
if (!upBefore)
|
|
57
|
+
throw new Error('GATEWAY_DID_NOT_LISTEN_ON_3099');
|
|
58
|
+
const t0 = Date.now();
|
|
59
|
+
await gateway.shutdown('TEST');
|
|
60
|
+
const elapsed = Date.now() - t0;
|
|
61
|
+
if (elapsed > 3000)
|
|
62
|
+
throw new Error(`SHUTDOWN_TOO_SLOW: ${elapsed}ms`);
|
|
63
|
+
const upAfter = await probe(3099);
|
|
64
|
+
if (upAfter)
|
|
65
|
+
throw new Error('GATEWAY_STILL_LISTENING_AFTER_SHUTDOWN');
|
|
66
|
+
await gateway.shutdown('TEST_IDEMPOTENT');
|
|
67
|
+
console.log(`TEST_DAEMON_SIGTERM_OK (shutdown=${elapsed}ms)`);
|
|
68
|
+
}
|
|
69
|
+
main().catch((err) => {
|
|
70
|
+
console.error('TEST_DAEMON_SIGTERM_FAIL:', err?.message || err);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
});
|