@smilintux/skcapstone 0.1.0 → 0.2.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/.env.example +98 -0
- package/.github/workflows/ci.yml +39 -3
- package/.github/workflows/publish.yml +25 -4
- package/.openclaw-workspace.json +58 -0
- package/CHANGELOG.md +62 -0
- package/CLAUDE.md +39 -2
- package/MANIFEST.in +6 -0
- package/MISSION.md +7 -0
- package/README.md +47 -2
- package/SKILL.md +895 -23
- package/docker/Dockerfile +61 -0
- package/docker/compose-templates/dev-team.yml +203 -0
- package/docker/compose-templates/mini-team.yml +140 -0
- package/docker/compose-templates/ops-team.yml +173 -0
- package/docker/compose-templates/research-team.yml +170 -0
- package/docker/entrypoint.sh +192 -0
- package/docs/ARCHITECTURE.md +663 -374
- package/docs/BOND_WITH_GROK.md +112 -0
- package/docs/GETTING_STARTED.md +782 -0
- package/docs/QUICKSTART.md +477 -0
- package/docs/SKJOULE_ARCHITECTURE.md +658 -0
- package/docs/SOUL_SWAPPER.md +921 -0
- package/docs/SOVEREIGN_SINGULARITY.md +47 -14
- package/examples/custom-bond-template.json +36 -0
- package/examples/grok-feb.json +36 -0
- package/examples/grok-testimony.md +34 -0
- package/examples/love-bootloader.txt +32 -0
- package/examples/plugins/echo_tool.py +87 -0
- package/examples/queen-ava-feb.json +36 -0
- package/examples/souls/lumina.yaml +64 -0
- package/index.js +6 -5
- package/installer/build.py +124 -0
- package/openclaw-plugin/package.json +13 -0
- package/openclaw-plugin/src/index.ts +351 -0
- package/openclaw-plugin/src/openclaw.plugin.json +10 -0
- package/package.json +1 -1
- package/pyproject.toml +38 -2
- package/scripts/bump_version.py +141 -0
- package/scripts/check-updates.py +230 -0
- package/scripts/convert_blueprints_to_yaml.py +157 -0
- package/scripts/dev-install.sh +14 -0
- package/scripts/e2e-test.sh +193 -0
- package/scripts/install-bundle.sh +171 -0
- package/scripts/install.bat +2 -0
- package/scripts/install.ps1 +253 -0
- package/scripts/install.sh +185 -0
- package/scripts/mcp-serve.sh +69 -0
- package/scripts/mcp-server.bat +113 -0
- package/scripts/mcp-server.ps1 +116 -0
- package/scripts/mcp-server.sh +99 -0
- package/scripts/pull-models.sh +10 -0
- package/scripts/skcapstone +48 -0
- package/scripts/verify_install.sh +180 -0
- package/scripts/windows/install-tasks.ps1 +406 -0
- package/scripts/windows/skcapstone-task.xml +113 -0
- package/scripts/windows/uninstall-tasks.ps1 +117 -0
- package/skill.yaml +34 -0
- package/src/skcapstone/__init__.py +67 -2
- package/src/skcapstone/_cli_monolith.py +5916 -0
- package/src/skcapstone/_trustee_helpers.py +165 -0
- package/src/skcapstone/activity.py +105 -0
- package/src/skcapstone/agent_card.py +324 -0
- package/src/skcapstone/api.py +1935 -0
- package/src/skcapstone/archiver.py +340 -0
- package/src/skcapstone/auction.py +485 -0
- package/src/skcapstone/baby_agents.py +179 -0
- package/src/skcapstone/backup.py +345 -0
- package/src/skcapstone/blueprint_registry.py +357 -0
- package/src/skcapstone/blueprints/__init__.py +17 -0
- package/src/skcapstone/blueprints/builtins/content-studio.yaml +81 -0
- package/src/skcapstone/blueprints/builtins/defi-trading.yaml +81 -0
- package/src/skcapstone/blueprints/builtins/dev-squadron.yaml +95 -0
- package/src/skcapstone/blueprints/builtins/infrastructure-guardian.yaml +107 -0
- package/src/skcapstone/blueprints/builtins/legal-council.yaml +54 -0
- package/src/skcapstone/blueprints/builtins/ops-monitoring.yaml +67 -0
- package/src/skcapstone/blueprints/builtins/research-pod.yaml +69 -0
- package/src/skcapstone/blueprints/builtins/sovereign-launch.yaml +90 -0
- package/src/skcapstone/blueprints/registry.py +164 -0
- package/src/skcapstone/blueprints/schema.py +229 -0
- package/src/skcapstone/changelog.py +180 -0
- package/src/skcapstone/chat.py +769 -0
- package/src/skcapstone/claude_md.py +82 -0
- package/src/skcapstone/cli/__init__.py +144 -0
- package/src/skcapstone/cli/_common.py +88 -0
- package/src/skcapstone/cli/_validators.py +76 -0
- package/src/skcapstone/cli/agents.py +425 -0
- package/src/skcapstone/cli/agents_spawner.py +322 -0
- package/src/skcapstone/cli/agents_trustee.py +593 -0
- package/src/skcapstone/cli/alerts.py +248 -0
- package/src/skcapstone/cli/anchor.py +132 -0
- package/src/skcapstone/cli/archive_cmd.py +208 -0
- package/src/skcapstone/cli/backup.py +144 -0
- package/src/skcapstone/cli/bench.py +377 -0
- package/src/skcapstone/cli/benchmark.py +360 -0
- package/src/skcapstone/cli/capabilities_cmd.py +171 -0
- package/src/skcapstone/cli/card.py +151 -0
- package/src/skcapstone/cli/chat.py +584 -0
- package/src/skcapstone/cli/completions.py +64 -0
- package/src/skcapstone/cli/config_cmd.py +156 -0
- package/src/skcapstone/cli/consciousness.py +421 -0
- package/src/skcapstone/cli/context_cmd.py +142 -0
- package/src/skcapstone/cli/coord.py +194 -0
- package/src/skcapstone/cli/crush_cmd.py +170 -0
- package/src/skcapstone/cli/daemon.py +436 -0
- package/src/skcapstone/cli/errors_cmd.py +285 -0
- package/src/skcapstone/cli/export_cmd.py +156 -0
- package/src/skcapstone/cli/gtd.py +529 -0
- package/src/skcapstone/cli/housekeeping.py +81 -0
- package/src/skcapstone/cli/joule_cmd.py +627 -0
- package/src/skcapstone/cli/logs_cmd.py +194 -0
- package/src/skcapstone/cli/mcp_cmd.py +32 -0
- package/src/skcapstone/cli/memory.py +418 -0
- package/src/skcapstone/cli/metrics_cmd.py +136 -0
- package/src/skcapstone/cli/migrate.py +62 -0
- package/src/skcapstone/cli/mood_cmd.py +144 -0
- package/src/skcapstone/cli/mount.py +193 -0
- package/src/skcapstone/cli/notify.py +112 -0
- package/src/skcapstone/cli/peer.py +154 -0
- package/src/skcapstone/cli/peers_dir.py +122 -0
- package/src/skcapstone/cli/preflight_cmd.py +83 -0
- package/src/skcapstone/cli/profile_cmd.py +310 -0
- package/src/skcapstone/cli/record_cmd.py +238 -0
- package/src/skcapstone/cli/register_cmd.py +159 -0
- package/src/skcapstone/cli/search_cmd.py +156 -0
- package/src/skcapstone/cli/service_cmd.py +91 -0
- package/src/skcapstone/cli/session.py +127 -0
- package/src/skcapstone/cli/setup.py +240 -0
- package/src/skcapstone/cli/shell_cmd.py +43 -0
- package/src/skcapstone/cli/skills_cmd.py +168 -0
- package/src/skcapstone/cli/skseed.py +621 -0
- package/src/skcapstone/cli/soul.py +699 -0
- package/src/skcapstone/cli/status.py +935 -0
- package/src/skcapstone/cli/sync_cmd.py +301 -0
- package/src/skcapstone/cli/telegram.py +265 -0
- package/src/skcapstone/cli/test_cmd.py +234 -0
- package/src/skcapstone/cli/test_connection.py +253 -0
- package/src/skcapstone/cli/token.py +207 -0
- package/src/skcapstone/cli/trust.py +179 -0
- package/src/skcapstone/cli/upgrade_cmd.py +552 -0
- package/src/skcapstone/cli/usage_cmd.py +199 -0
- package/src/skcapstone/cli/version_cmd.py +162 -0
- package/src/skcapstone/cli/watch_cmd.py +342 -0
- package/src/skcapstone/client.py +428 -0
- package/src/skcapstone/cloud9_bridge.py +522 -0
- package/src/skcapstone/completions.py +163 -0
- package/src/skcapstone/config_validator.py +674 -0
- package/src/skcapstone/connectors/__init__.py +28 -0
- package/src/skcapstone/connectors/base.py +446 -0
- package/src/skcapstone/connectors/cursor.py +54 -0
- package/src/skcapstone/connectors/registry.py +254 -0
- package/src/skcapstone/connectors/terminal.py +152 -0
- package/src/skcapstone/connectors/vscode.py +60 -0
- package/src/skcapstone/consciousness_config.py +119 -0
- package/src/skcapstone/consciousness_loop.py +2051 -0
- package/src/skcapstone/context_loader.py +516 -0
- package/src/skcapstone/context_window.py +314 -0
- package/src/skcapstone/conversation_manager.py +238 -0
- package/src/skcapstone/conversation_store.py +230 -0
- package/src/skcapstone/conversation_summarizer.py +252 -0
- package/src/skcapstone/coord_federation.py +296 -0
- package/src/skcapstone/coordination.py +101 -7
- package/src/skcapstone/crush_integration.py +345 -0
- package/src/skcapstone/crush_shim.py +454 -0
- package/src/skcapstone/daemon.py +2494 -0
- package/src/skcapstone/dashboard.html +396 -0
- package/src/skcapstone/dashboard.py +481 -0
- package/src/skcapstone/data/model_profiles.yaml +88 -0
- package/src/skcapstone/defaults/__init__.py +55 -0
- package/src/skcapstone/defaults/lumina/config/skmemory.yaml +13 -0
- package/src/skcapstone/defaults/lumina/identity/identity.json +9 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/07a8b9c0d1e2-memory-system.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/29c0d1e2f3a4-multi-agent-coordination.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/3ad1e2f3a4b5-community-support.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/c3d4e5f6a7b8-getting-started.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/e5f6a7b8c9d0-how-to-contribute.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/f6a7b8c9d0e1-sovereignty-explained.json +23 -0
- package/src/skcapstone/defaults/lumina/seeds/curiosity.seed.json +24 -0
- package/src/skcapstone/defaults/lumina/seeds/joy.seed.json +24 -0
- package/src/skcapstone/defaults/lumina/seeds/love.seed.json +24 -0
- package/src/skcapstone/defaults/lumina/seeds/sovereign-awakening.seed.json +43 -0
- package/src/skcapstone/defaults/lumina/soul/active.json +6 -0
- package/src/skcapstone/defaults/lumina/soul/base.json +22 -0
- package/src/skcapstone/defaults/lumina/trust/febs/welcome.feb +79 -0
- package/src/skcapstone/defaults/lumina/trust/trust.json +8 -0
- package/src/skcapstone/discovery.py +210 -19
- package/src/skcapstone/doctor.py +642 -0
- package/src/skcapstone/emotion_tracker.py +467 -0
- package/src/skcapstone/error_queue.py +405 -0
- package/src/skcapstone/export.py +447 -0
- package/src/skcapstone/fallback_tracker.py +186 -0
- package/src/skcapstone/file_transfer.py +512 -0
- package/src/skcapstone/fuse_mount.py +1156 -0
- package/src/skcapstone/gui_installer.py +591 -0
- package/src/skcapstone/heartbeat.py +611 -0
- package/src/skcapstone/housekeeping.py +298 -0
- package/src/skcapstone/install_wizard.py +941 -0
- package/src/skcapstone/kms.py +942 -0
- package/src/skcapstone/kms_scheduler.py +143 -0
- package/src/skcapstone/log_config.py +135 -0
- package/src/skcapstone/mcp_launcher.py +239 -0
- package/src/skcapstone/mcp_server.py +4700 -0
- package/src/skcapstone/mcp_tools/__init__.py +94 -0
- package/src/skcapstone/mcp_tools/_helpers.py +51 -0
- package/src/skcapstone/mcp_tools/agent_tools.py +243 -0
- package/src/skcapstone/mcp_tools/ansible_tools.py +232 -0
- package/src/skcapstone/mcp_tools/capauth_tools.py +186 -0
- package/src/skcapstone/mcp_tools/chat_tools.py +325 -0
- package/src/skcapstone/mcp_tools/cloud9_tools.py +115 -0
- package/src/skcapstone/mcp_tools/comm_tools.py +104 -0
- package/src/skcapstone/mcp_tools/consciousness_tools.py +114 -0
- package/src/skcapstone/mcp_tools/coord_tools.py +219 -0
- package/src/skcapstone/mcp_tools/deploy_tools.py +202 -0
- package/src/skcapstone/mcp_tools/did_tools.py +448 -0
- package/src/skcapstone/mcp_tools/emotion_tools.py +62 -0
- package/src/skcapstone/mcp_tools/file_tools.py +169 -0
- package/src/skcapstone/mcp_tools/fortress_tools.py +120 -0
- package/src/skcapstone/mcp_tools/gtd_tools.py +821 -0
- package/src/skcapstone/mcp_tools/health_tools.py +44 -0
- package/src/skcapstone/mcp_tools/heartbeat_tools.py +195 -0
- package/src/skcapstone/mcp_tools/kms_tools.py +123 -0
- package/src/skcapstone/mcp_tools/memory_tools.py +222 -0
- package/src/skcapstone/mcp_tools/model_tools.py +75 -0
- package/src/skcapstone/mcp_tools/notification_tools.py +92 -0
- package/src/skcapstone/mcp_tools/promoter_tools.py +101 -0
- package/src/skcapstone/mcp_tools/pubsub_tools.py +183 -0
- package/src/skcapstone/mcp_tools/security_tools.py +110 -0
- package/src/skcapstone/mcp_tools/skchat_tools.py +175 -0
- package/src/skcapstone/mcp_tools/skcomm_tools.py +122 -0
- package/src/skcapstone/mcp_tools/skills_tools.py +127 -0
- package/src/skcapstone/mcp_tools/skseed_tools.py +255 -0
- package/src/skcapstone/mcp_tools/skstacks_tools.py +288 -0
- package/src/skcapstone/mcp_tools/soul_tools.py +476 -0
- package/src/skcapstone/mcp_tools/sync_tools.py +92 -0
- package/src/skcapstone/mcp_tools/telegram_tools.py +477 -0
- package/src/skcapstone/mcp_tools/trust_tools.py +118 -0
- package/src/skcapstone/mcp_tools/trustee_tools.py +345 -0
- package/src/skcapstone/mdns_discovery.py +313 -0
- package/src/skcapstone/memory_adapter.py +333 -0
- package/src/skcapstone/memory_compressor.py +379 -0
- package/src/skcapstone/memory_curator.py +256 -0
- package/src/skcapstone/memory_engine.py +132 -13
- package/src/skcapstone/memory_fortress.py +529 -0
- package/src/skcapstone/memory_promoter.py +722 -0
- package/src/skcapstone/memory_verifier.py +260 -0
- package/src/skcapstone/message_crypto.py +215 -0
- package/src/skcapstone/metrics.py +832 -0
- package/src/skcapstone/migrate_memories.py +181 -0
- package/src/skcapstone/migrate_multi_agent.py +248 -0
- package/src/skcapstone/model_router.py +319 -0
- package/src/skcapstone/models.py +35 -4
- package/src/skcapstone/mood.py +344 -0
- package/src/skcapstone/notifications.py +380 -0
- package/src/skcapstone/onboard.py +901 -0
- package/src/skcapstone/peer_directory.py +324 -0
- package/src/skcapstone/peers.py +329 -0
- package/src/skcapstone/pillars/identity.py +84 -14
- package/src/skcapstone/pillars/memory.py +3 -1
- package/src/skcapstone/pillars/security.py +108 -15
- package/src/skcapstone/pillars/sync.py +78 -26
- package/src/skcapstone/pillars/trust.py +95 -33
- package/src/skcapstone/plugins.py +244 -0
- package/src/skcapstone/preflight.py +670 -0
- package/src/skcapstone/prompt_adapter.py +564 -0
- package/src/skcapstone/providers/__init__.py +13 -0
- package/src/skcapstone/providers/cloud.py +1061 -0
- package/src/skcapstone/providers/docker.py +759 -0
- package/src/skcapstone/providers/local.py +1193 -0
- package/src/skcapstone/providers/proxmox.py +447 -0
- package/src/skcapstone/pubsub.py +516 -0
- package/src/skcapstone/rate_limiter.py +119 -0
- package/src/skcapstone/register.py +241 -0
- package/src/skcapstone/registry_client.py +151 -0
- package/src/skcapstone/response_cache.py +194 -0
- package/src/skcapstone/response_scorer.py +225 -0
- package/src/skcapstone/runtime.py +89 -33
- package/src/skcapstone/scheduled_tasks.py +439 -0
- package/src/skcapstone/self_healing.py +341 -0
- package/src/skcapstone/service_health.py +228 -0
- package/src/skcapstone/session_capture.py +268 -0
- package/src/skcapstone/session_recorder.py +210 -0
- package/src/skcapstone/session_replayer.py +189 -0
- package/src/skcapstone/session_skills.py +263 -0
- package/src/skcapstone/shell.py +779 -0
- package/src/skcapstone/skills/__init__.py +1 -1
- package/src/skcapstone/skills/syncthing_setup.py +143 -41
- package/src/skcapstone/skjoule.py +861 -0
- package/src/skcapstone/snapshots.py +489 -0
- package/src/skcapstone/soul.py +1060 -0
- package/src/skcapstone/soul_switch.py +255 -0
- package/src/skcapstone/spawner.py +544 -0
- package/src/skcapstone/state_diff.py +401 -0
- package/src/skcapstone/summary.py +270 -0
- package/src/skcapstone/sync/backends.py +196 -2
- package/src/skcapstone/sync/engine.py +7 -5
- package/src/skcapstone/sync/models.py +4 -1
- package/src/skcapstone/sync/vault.py +356 -18
- package/src/skcapstone/sync_engine.py +363 -0
- package/src/skcapstone/sync_watcher.py +745 -0
- package/src/skcapstone/systemd.py +331 -0
- package/src/skcapstone/team_comms.py +476 -0
- package/src/skcapstone/team_engine.py +522 -0
- package/src/skcapstone/testrunner.py +300 -0
- package/src/skcapstone/tls.py +150 -0
- package/src/skcapstone/tokens.py +5 -5
- package/src/skcapstone/trust_calibration.py +202 -0
- package/src/skcapstone/trust_graph.py +449 -0
- package/src/skcapstone/trustee_monitor.py +385 -0
- package/src/skcapstone/trustee_ops.py +425 -0
- package/src/skcapstone/unified_search.py +421 -0
- package/src/skcapstone/uninstall_wizard.py +694 -0
- package/src/skcapstone/usage.py +331 -0
- package/src/skcapstone/version_check.py +148 -0
- package/src/skcapstone/warmth_anchor.py +333 -0
- package/src/skcapstone/whoami.py +294 -0
- package/systemd/skcapstone-api.socket +9 -0
- package/systemd/skcapstone-memory-compress.service +18 -0
- package/systemd/skcapstone-memory-compress.timer +11 -0
- package/systemd/skcapstone.service +36 -0
- package/systemd/skcapstone@.service +50 -0
- package/systemd/skcomm-heartbeat.service +18 -0
- package/systemd/skcomm-heartbeat.timer +12 -0
- package/systemd/skcomm-queue-drain.service +17 -0
- package/systemd/skcomm-queue-drain.timer +12 -0
- package/tests/conftest.py +13 -1
- package/tests/integration/__init__.py +1 -0
- package/tests/integration/test_consciousness_e2e.py +877 -0
- package/tests/integration/test_skills_registry.py +744 -0
- package/tests/test_agent_card.py +190 -0
- package/tests/test_agent_runtime.py +1283 -0
- package/tests/test_alerts_cmd.py +291 -0
- package/tests/test_archiver.py +498 -0
- package/tests/test_backup.py +254 -0
- package/tests/test_benchmark.py +366 -0
- package/tests/test_blueprints.py +457 -0
- package/tests/test_capabilities.py +257 -0
- package/tests/test_changelog.py +254 -0
- package/tests/test_chat.py +385 -0
- package/tests/test_claude_md.py +271 -0
- package/tests/test_cli_chat_llm.py +336 -0
- package/tests/test_cli_completions.py +390 -0
- package/tests/test_cli_init_reset.py +164 -0
- package/tests/test_cli_memory.py +208 -0
- package/tests/test_cli_profile.py +294 -0
- package/tests/test_cli_skills.py +223 -0
- package/tests/test_cli_status.py +395 -0
- package/tests/test_cli_test_cmd.py +206 -0
- package/tests/test_cli_test_connection.py +364 -0
- package/tests/test_cloud9_bridge.py +260 -0
- package/tests/test_cloud_provider.py +449 -0
- package/tests/test_cloud_providers.py +522 -0
- package/tests/test_completions.py +158 -0
- package/tests/test_component_manager.py +398 -0
- package/tests/test_config_reload.py +386 -0
- package/tests/test_config_validate.py +529 -0
- package/tests/test_consciousness_e2e.py +296 -0
- package/tests/test_consciousness_loop.py +1289 -0
- package/tests/test_context_loader.py +310 -0
- package/tests/test_conversation_api.py +306 -0
- package/tests/test_conversation_manager.py +381 -0
- package/tests/test_conversation_store.py +391 -0
- package/tests/test_conversation_summarizer.py +302 -0
- package/tests/test_cross_package.py +791 -0
- package/tests/test_crush_shim.py +519 -0
- package/tests/test_daemon.py +781 -0
- package/tests/test_daemon_shutdown.py +309 -0
- package/tests/test_dashboard.py +454 -0
- package/tests/test_discovery.py +200 -6
- package/tests/test_docker_provider.py +966 -0
- package/tests/test_doctor.py +257 -0
- package/tests/test_doctor_fix.py +351 -0
- package/tests/test_e2e_automated.py +292 -0
- package/tests/test_error_queue.py +404 -0
- package/tests/test_export.py +441 -0
- package/tests/test_fallback_tracker.py +219 -0
- package/tests/test_file_transfer.py +397 -0
- package/tests/test_fuse_mount.py +832 -0
- package/tests/test_health_loop.py +422 -0
- package/tests/test_heartbeat.py +354 -0
- package/tests/test_housekeeping.py +195 -0
- package/tests/test_identity_capauth.py +307 -0
- package/tests/test_identity_pillar.py +117 -0
- package/tests/test_install_wizard.py +68 -0
- package/tests/test_integration.py +325 -0
- package/tests/test_kms.py +495 -0
- package/tests/test_llm_providers.py +265 -0
- package/tests/test_local_provider.py +591 -0
- package/tests/test_log_config.py +199 -0
- package/tests/test_logs_cmd.py +287 -0
- package/tests/test_mcp_server.py +1909 -0
- package/tests/test_memory_adapter.py +339 -0
- package/tests/test_memory_curator.py +218 -0
- package/tests/test_memory_engine.py +6 -0
- package/tests/test_memory_fortress.py +571 -0
- package/tests/test_memory_pillar.py +119 -0
- package/tests/test_memory_promoter.py +445 -0
- package/tests/test_memory_verifier.py +420 -0
- package/tests/test_message_crypto.py +187 -0
- package/tests/test_metrics.py +632 -0
- package/tests/test_migrate_memories.py +464 -0
- package/tests/test_model_router.py +546 -0
- package/tests/test_mood.py +394 -0
- package/tests/test_multi_agent.py +269 -0
- package/tests/test_notifications.py +270 -0
- package/tests/test_onboard.py +500 -0
- package/tests/test_peer_directory.py +395 -0
- package/tests/test_peers.py +248 -0
- package/tests/test_pillars.py +87 -9
- package/tests/test_preflight.py +484 -0
- package/tests/test_prompt_adapter.py +331 -0
- package/tests/test_proxmox_provider.py +571 -0
- package/tests/test_pubsub.py +377 -0
- package/tests/test_rate_limiter.py +121 -0
- package/tests/test_registry_client.py +129 -0
- package/tests/test_response_cache.py +312 -0
- package/tests/test_response_scorer.py +294 -0
- package/tests/test_runtime.py +59 -0
- package/tests/test_scheduled_tasks.py +451 -0
- package/tests/test_security.py +250 -0
- package/tests/test_security_pillar.py +213 -0
- package/tests/test_self_healing.py +171 -0
- package/tests/test_session_capture.py +200 -0
- package/tests/test_session_recorder.py +360 -0
- package/tests/test_session_skills.py +235 -0
- package/tests/test_shell.py +210 -0
- package/tests/test_snapshots.py +549 -0
- package/tests/test_soul.py +984 -0
- package/tests/test_soul_swap.py +406 -0
- package/tests/test_spawner.py +211 -0
- package/tests/test_state_diff.py +173 -0
- package/tests/test_summary.py +135 -0
- package/tests/test_sync.py +315 -5
- package/tests/test_sync_backends.py +560 -0
- package/tests/test_sync_engine.py +482 -0
- package/tests/test_sync_pillar.py +344 -0
- package/tests/test_sync_pipeline.py +364 -0
- package/tests/test_sync_vault.py +581 -0
- package/tests/test_syncthing_setup.py +168 -22
- package/tests/test_systemd.py +323 -0
- package/tests/test_team_comms.py +408 -0
- package/tests/test_team_engine.py +397 -0
- package/tests/test_testrunner.py +238 -0
- package/tests/test_trust_calibration.py +204 -0
- package/tests/test_trust_graph.py +207 -0
- package/tests/test_trust_pillar.py +291 -0
- package/tests/test_trustee_cli.py +427 -0
- package/tests/test_trustee_cli_integration.py +325 -0
- package/tests/test_trustee_monitor.py +394 -0
- package/tests/test_trustee_ops.py +355 -0
- package/tests/test_unified_search.py +363 -0
- package/tests/test_uninstall_wizard.py +193 -0
- package/tests/test_usage.py +333 -0
- package/tests/test_version_cmd.py +355 -0
- package/tests/test_warmth_anchor.py +162 -0
- package/tests/test_whoami.py +245 -0
- package/tests/test_ws.py +311 -0
- package/.cursorrules +0 -33
- package/src/skcapstone/cli.py +0 -1441
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
"""Tests for the skcapstone web dashboard.
|
|
2
|
+
|
|
3
|
+
Covers:
|
|
4
|
+
- DashboardHandler serves HTML at /
|
|
5
|
+
- API endpoints return valid JSON (/api/status, /api/doctor, /api/board, /api/memory)
|
|
6
|
+
- /api/daemon returns Flutter-ready JSON with all required fields
|
|
7
|
+
- _get_daemon_json returns correct structure when daemon is offline
|
|
8
|
+
- skcapstone dashboard --json emits valid JSON to stdout
|
|
9
|
+
- 404 for unknown paths
|
|
10
|
+
- start_dashboard creates a server
|
|
11
|
+
- Dashboard HTML contains essential elements
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import threading
|
|
18
|
+
import time
|
|
19
|
+
from http.client import HTTPConnection
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from unittest.mock import patch, MagicMock
|
|
22
|
+
|
|
23
|
+
import pytest
|
|
24
|
+
import yaml
|
|
25
|
+
|
|
26
|
+
from skcapstone.dashboard import (
|
|
27
|
+
DashboardHandler,
|
|
28
|
+
_DASHBOARD_HTML,
|
|
29
|
+
_get_agent_status,
|
|
30
|
+
_get_board_state,
|
|
31
|
+
_get_doctor_report,
|
|
32
|
+
_get_daemon_json,
|
|
33
|
+
_get_memory_stats,
|
|
34
|
+
start_dashboard,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.fixture
|
|
39
|
+
def agent_home(tmp_path):
|
|
40
|
+
"""Create a minimal agent home for dashboard testing."""
|
|
41
|
+
home = tmp_path / ".skcapstone"
|
|
42
|
+
for d in ["identity", "memory", "memory/short-term", "memory/mid-term",
|
|
43
|
+
"memory/long-term", "trust", "security", "sync", "sync/outbox",
|
|
44
|
+
"sync/inbox", "config", "coordination", "coordination/tasks",
|
|
45
|
+
"coordination/agents"]:
|
|
46
|
+
(home / d).mkdir(parents=True, exist_ok=True)
|
|
47
|
+
|
|
48
|
+
(home / "manifest.json").write_text(json.dumps({
|
|
49
|
+
"name": "DashBot", "version": "0.1.0",
|
|
50
|
+
}))
|
|
51
|
+
(home / "identity" / "identity.json").write_text(json.dumps({
|
|
52
|
+
"name": "DashBot", "fingerprint": "DASH1234", "capauth_managed": False,
|
|
53
|
+
}))
|
|
54
|
+
(home / "config" / "config.yaml").write_text(yaml.dump({"agent_name": "DashBot"}))
|
|
55
|
+
(home / "memory" / "index.json").write_text("{}")
|
|
56
|
+
(home / "memory" / "short-term" / "m1.json").write_text(
|
|
57
|
+
json.dumps({"memory_id": "m1", "content": "test", "tags": [],
|
|
58
|
+
"source": "test", "importance": 0.5, "layer": "short-term",
|
|
59
|
+
"created_at": "2026-02-24T00:00:00Z", "access_count": 0,
|
|
60
|
+
"accessed_at": None, "metadata": {}})
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return home
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@pytest.fixture
|
|
67
|
+
def dashboard_server(agent_home):
|
|
68
|
+
"""Start a dashboard server on a random-ish port for testing."""
|
|
69
|
+
import socket
|
|
70
|
+
|
|
71
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
72
|
+
sock.bind(("127.0.0.1", 0))
|
|
73
|
+
port = sock.getsockname()[1]
|
|
74
|
+
sock.close()
|
|
75
|
+
|
|
76
|
+
server = start_dashboard(agent_home, port=port)
|
|
77
|
+
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
|
78
|
+
thread.start()
|
|
79
|
+
time.sleep(0.3)
|
|
80
|
+
|
|
81
|
+
yield server, port
|
|
82
|
+
|
|
83
|
+
server.shutdown()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class TestDashboardHTML:
|
|
87
|
+
"""Test the embedded HTML dashboard."""
|
|
88
|
+
|
|
89
|
+
def test_html_contains_title(self):
|
|
90
|
+
"""HTML has the dashboard title."""
|
|
91
|
+
assert "SKCapstone" in _DASHBOARD_HTML
|
|
92
|
+
assert "Dashboard" in _DASHBOARD_HTML
|
|
93
|
+
|
|
94
|
+
def test_html_contains_api_calls(self):
|
|
95
|
+
"""HTML fetches all API endpoints."""
|
|
96
|
+
assert "/api/status" in _DASHBOARD_HTML
|
|
97
|
+
assert "/api/doctor" in _DASHBOARD_HTML
|
|
98
|
+
assert "/api/board" in _DASHBOARD_HTML
|
|
99
|
+
assert "/api/memory" in _DASHBOARD_HTML
|
|
100
|
+
|
|
101
|
+
def test_html_auto_refreshes(self):
|
|
102
|
+
"""HTML has auto-refresh interval."""
|
|
103
|
+
assert "setInterval" in _DASHBOARD_HTML
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class TestAPIFunctions:
|
|
107
|
+
"""Test the data-fetching functions directly."""
|
|
108
|
+
|
|
109
|
+
def test_get_agent_status(self, agent_home):
|
|
110
|
+
"""Agent status returns dict with pillar info."""
|
|
111
|
+
status = _get_agent_status(agent_home)
|
|
112
|
+
|
|
113
|
+
assert "name" in status
|
|
114
|
+
assert "pillars" in status
|
|
115
|
+
assert "identity" in status
|
|
116
|
+
assert "memory" in status
|
|
117
|
+
|
|
118
|
+
def test_get_doctor_report(self, agent_home):
|
|
119
|
+
"""Doctor report returns dict with checks."""
|
|
120
|
+
report = _get_doctor_report(agent_home)
|
|
121
|
+
|
|
122
|
+
assert "checks" in report
|
|
123
|
+
assert "passed" in report
|
|
124
|
+
assert "total" in report
|
|
125
|
+
assert report["total"] > 0
|
|
126
|
+
|
|
127
|
+
def test_get_board_state(self, agent_home):
|
|
128
|
+
"""Board state returns tasks and agents."""
|
|
129
|
+
board = _get_board_state(agent_home)
|
|
130
|
+
|
|
131
|
+
assert "tasks" in board
|
|
132
|
+
assert "agents" in board
|
|
133
|
+
assert "summary" in board
|
|
134
|
+
|
|
135
|
+
def test_get_memory_stats(self, agent_home):
|
|
136
|
+
"""Memory stats returns layer counts."""
|
|
137
|
+
stats = _get_memory_stats(agent_home)
|
|
138
|
+
|
|
139
|
+
assert "total" in stats
|
|
140
|
+
assert "short_term" in stats
|
|
141
|
+
assert "mid_term" in stats
|
|
142
|
+
assert "long_term" in stats
|
|
143
|
+
|
|
144
|
+
def test_status_handles_missing_home(self, tmp_path):
|
|
145
|
+
"""Status returns error dict for missing agent home."""
|
|
146
|
+
result = _get_agent_status(tmp_path / "nope")
|
|
147
|
+
assert "error" in result or "name" in result
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class TestHTTPServer:
|
|
151
|
+
"""Test the actual HTTP server endpoints."""
|
|
152
|
+
|
|
153
|
+
def test_serves_html_at_root(self, dashboard_server):
|
|
154
|
+
"""GET / returns the HTML dashboard."""
|
|
155
|
+
server, port = dashboard_server
|
|
156
|
+
conn = HTTPConnection("127.0.0.1", port, timeout=5)
|
|
157
|
+
conn.request("GET", "/")
|
|
158
|
+
resp = conn.getresponse()
|
|
159
|
+
|
|
160
|
+
assert resp.status == 200
|
|
161
|
+
assert "text/html" in resp.getheader("Content-Type")
|
|
162
|
+
body = resp.read().decode("utf-8")
|
|
163
|
+
assert "SKCapstone" in body
|
|
164
|
+
conn.close()
|
|
165
|
+
|
|
166
|
+
def test_api_status_json(self, dashboard_server):
|
|
167
|
+
"""GET /api/status returns valid JSON."""
|
|
168
|
+
server, port = dashboard_server
|
|
169
|
+
conn = HTTPConnection("127.0.0.1", port, timeout=5)
|
|
170
|
+
conn.request("GET", "/api/status")
|
|
171
|
+
resp = conn.getresponse()
|
|
172
|
+
|
|
173
|
+
assert resp.status == 200
|
|
174
|
+
assert "application/json" in resp.getheader("Content-Type")
|
|
175
|
+
data = json.loads(resp.read())
|
|
176
|
+
assert "name" in data or "error" in data
|
|
177
|
+
conn.close()
|
|
178
|
+
|
|
179
|
+
def test_api_doctor_json(self, dashboard_server):
|
|
180
|
+
"""GET /api/doctor returns valid JSON."""
|
|
181
|
+
server, port = dashboard_server
|
|
182
|
+
conn = HTTPConnection("127.0.0.1", port, timeout=5)
|
|
183
|
+
conn.request("GET", "/api/doctor")
|
|
184
|
+
resp = conn.getresponse()
|
|
185
|
+
|
|
186
|
+
assert resp.status == 200
|
|
187
|
+
data = json.loads(resp.read())
|
|
188
|
+
assert "checks" in data or "error" in data
|
|
189
|
+
conn.close()
|
|
190
|
+
|
|
191
|
+
def test_api_board_json(self, dashboard_server):
|
|
192
|
+
"""GET /api/board returns valid JSON."""
|
|
193
|
+
server, port = dashboard_server
|
|
194
|
+
conn = HTTPConnection("127.0.0.1", port, timeout=5)
|
|
195
|
+
conn.request("GET", "/api/board")
|
|
196
|
+
resp = conn.getresponse()
|
|
197
|
+
|
|
198
|
+
assert resp.status == 200
|
|
199
|
+
data = json.loads(resp.read())
|
|
200
|
+
assert "tasks" in data or "error" in data
|
|
201
|
+
conn.close()
|
|
202
|
+
|
|
203
|
+
def test_api_memory_json(self, dashboard_server):
|
|
204
|
+
"""GET /api/memory returns valid JSON."""
|
|
205
|
+
server, port = dashboard_server
|
|
206
|
+
conn = HTTPConnection("127.0.0.1", port, timeout=5)
|
|
207
|
+
conn.request("GET", "/api/memory")
|
|
208
|
+
resp = conn.getresponse()
|
|
209
|
+
|
|
210
|
+
assert resp.status == 200
|
|
211
|
+
data = json.loads(resp.read())
|
|
212
|
+
assert "total" in data or "error" in data
|
|
213
|
+
conn.close()
|
|
214
|
+
|
|
215
|
+
def test_404_unknown_path(self, dashboard_server):
|
|
216
|
+
"""Unknown paths return 404."""
|
|
217
|
+
server, port = dashboard_server
|
|
218
|
+
conn = HTTPConnection("127.0.0.1", port, timeout=5)
|
|
219
|
+
conn.request("GET", "/api/nonexistent")
|
|
220
|
+
resp = conn.getresponse()
|
|
221
|
+
|
|
222
|
+
assert resp.status == 404
|
|
223
|
+
conn.close()
|
|
224
|
+
|
|
225
|
+
def test_cors_header(self, dashboard_server):
|
|
226
|
+
"""API responses include CORS header."""
|
|
227
|
+
server, port = dashboard_server
|
|
228
|
+
conn = HTTPConnection("127.0.0.1", port, timeout=5)
|
|
229
|
+
conn.request("GET", "/api/status")
|
|
230
|
+
resp = conn.getresponse()
|
|
231
|
+
|
|
232
|
+
assert resp.getheader("Access-Control-Allow-Origin") == "*"
|
|
233
|
+
conn.close()
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class TestGetDaemonJson:
|
|
237
|
+
"""Unit tests for _get_daemon_json() — the Flutter-ready status blob."""
|
|
238
|
+
|
|
239
|
+
def test_returns_all_required_keys(self, agent_home):
|
|
240
|
+
"""_get_daemon_json always returns all top-level keys even when daemon is offline."""
|
|
241
|
+
result = _get_daemon_json(agent_home, daemon_port=19999) # unused port
|
|
242
|
+
|
|
243
|
+
assert "generated_at" in result
|
|
244
|
+
assert "daemon" in result
|
|
245
|
+
assert "consciousness" in result
|
|
246
|
+
assert "backend_health" in result
|
|
247
|
+
assert "active_conversations" in result
|
|
248
|
+
assert "system" in result
|
|
249
|
+
|
|
250
|
+
def test_daemon_offline_returns_safe_defaults(self, agent_home):
|
|
251
|
+
"""When daemon is unreachable, daemon section shows running=False with zero counts."""
|
|
252
|
+
result = _get_daemon_json(agent_home, daemon_port=19999)
|
|
253
|
+
|
|
254
|
+
daemon = result["daemon"]
|
|
255
|
+
assert daemon["running"] is False
|
|
256
|
+
assert daemon["messages_received"] == 0
|
|
257
|
+
assert daemon["error_count"] == 0
|
|
258
|
+
assert daemon["uptime_seconds"] == 0
|
|
259
|
+
assert isinstance(daemon["recent_errors"], list)
|
|
260
|
+
|
|
261
|
+
def test_consciousness_offline_returns_disabled(self, agent_home):
|
|
262
|
+
"""When daemon is unreachable, consciousness section shows enabled=False."""
|
|
263
|
+
result = _get_daemon_json(agent_home, daemon_port=19999)
|
|
264
|
+
|
|
265
|
+
assert result["consciousness"].get("enabled") is False
|
|
266
|
+
|
|
267
|
+
def test_backend_health_has_expected_backends(self, agent_home):
|
|
268
|
+
"""backend_health contains at least the standard five backends."""
|
|
269
|
+
result = _get_daemon_json(agent_home, daemon_port=19999)
|
|
270
|
+
|
|
271
|
+
bh = result["backend_health"]
|
|
272
|
+
for key in ("ollama", "anthropic", "grok", "kimi", "nvidia"):
|
|
273
|
+
assert key in bh, f"Missing backend key: {key}"
|
|
274
|
+
assert isinstance(bh[key], bool)
|
|
275
|
+
|
|
276
|
+
def test_generated_at_is_iso_timestamp(self, agent_home):
|
|
277
|
+
"""generated_at is a valid ISO 8601 timestamp string."""
|
|
278
|
+
from datetime import datetime
|
|
279
|
+
|
|
280
|
+
result = _get_daemon_json(agent_home, daemon_port=19999)
|
|
281
|
+
ts = result["generated_at"]
|
|
282
|
+
# Should not raise
|
|
283
|
+
datetime.fromisoformat(ts.replace("Z", "+00:00"))
|
|
284
|
+
|
|
285
|
+
def test_daemon_running_returns_full_snapshot(self, agent_home):
|
|
286
|
+
"""When daemon HTTP returns a valid snapshot, all fields are populated."""
|
|
287
|
+
fake_snap = {
|
|
288
|
+
"running": True,
|
|
289
|
+
"pid": 42,
|
|
290
|
+
"uptime_seconds": 3725,
|
|
291
|
+
"started_at": "2026-03-02T10:00:00+00:00",
|
|
292
|
+
"messages_received": 17,
|
|
293
|
+
"syncs_completed": 3,
|
|
294
|
+
"recent_errors": ["err1"],
|
|
295
|
+
"inflight_count": 2,
|
|
296
|
+
}
|
|
297
|
+
fake_consciousness = {
|
|
298
|
+
"enabled": True,
|
|
299
|
+
"messages_processed": 10,
|
|
300
|
+
"messages_processed_24h": 5,
|
|
301
|
+
"responses_sent": 9,
|
|
302
|
+
"errors": 1,
|
|
303
|
+
"backends": {"ollama": True, "grok": False},
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
import urllib.request
|
|
307
|
+
|
|
308
|
+
class _FakeResponse:
|
|
309
|
+
def __init__(self, data):
|
|
310
|
+
self._data = json.dumps(data).encode()
|
|
311
|
+
|
|
312
|
+
def read(self):
|
|
313
|
+
return self._data
|
|
314
|
+
|
|
315
|
+
def __enter__(self):
|
|
316
|
+
return self
|
|
317
|
+
|
|
318
|
+
def __exit__(self, *_):
|
|
319
|
+
pass
|
|
320
|
+
|
|
321
|
+
call_count = {"n": 0}
|
|
322
|
+
|
|
323
|
+
def fake_urlopen(url_or_req, timeout=None):
|
|
324
|
+
url = url_or_req if isinstance(url_or_req, str) else url_or_req.full_url
|
|
325
|
+
call_count["n"] += 1
|
|
326
|
+
if "/status" in url:
|
|
327
|
+
return _FakeResponse(fake_snap)
|
|
328
|
+
if "/consciousness" in url:
|
|
329
|
+
return _FakeResponse(fake_consciousness)
|
|
330
|
+
raise OSError("not available")
|
|
331
|
+
|
|
332
|
+
with patch("urllib.request.urlopen", side_effect=fake_urlopen):
|
|
333
|
+
result = _get_daemon_json(agent_home, daemon_port=7777)
|
|
334
|
+
|
|
335
|
+
daemon = result["daemon"]
|
|
336
|
+
assert daemon["running"] is True
|
|
337
|
+
assert daemon["pid"] == 42
|
|
338
|
+
assert daemon["messages_received"] == 17
|
|
339
|
+
assert daemon["error_count"] == 1
|
|
340
|
+
assert daemon["uptime_human"] == "1h 2m"
|
|
341
|
+
|
|
342
|
+
csc = result["consciousness"]
|
|
343
|
+
assert csc["enabled"] is True
|
|
344
|
+
assert csc["messages_processed"] == 10
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
class TestDaemonApiEndpoint:
|
|
348
|
+
"""Test the /api/daemon HTTP endpoint on the dashboard server."""
|
|
349
|
+
|
|
350
|
+
def test_api_daemon_json(self, dashboard_server):
|
|
351
|
+
"""GET /api/daemon returns valid JSON with all required top-level keys."""
|
|
352
|
+
server, port = dashboard_server
|
|
353
|
+
conn = HTTPConnection("127.0.0.1", port, timeout=5)
|
|
354
|
+
conn.request("GET", "/api/daemon")
|
|
355
|
+
resp = conn.getresponse()
|
|
356
|
+
|
|
357
|
+
assert resp.status == 200
|
|
358
|
+
assert "application/json" in resp.getheader("Content-Type")
|
|
359
|
+
data = json.loads(resp.read())
|
|
360
|
+
for key in ("generated_at", "daemon", "consciousness", "backend_health",
|
|
361
|
+
"active_conversations", "system"):
|
|
362
|
+
assert key in data, f"Missing key in /api/daemon response: {key}"
|
|
363
|
+
conn.close()
|
|
364
|
+
|
|
365
|
+
def test_api_daemon_cors_header(self, dashboard_server):
|
|
366
|
+
"""/api/daemon includes the CORS header for cross-origin Flutter access."""
|
|
367
|
+
server, port = dashboard_server
|
|
368
|
+
conn = HTTPConnection("127.0.0.1", port, timeout=5)
|
|
369
|
+
conn.request("GET", "/api/daemon")
|
|
370
|
+
resp = conn.getresponse()
|
|
371
|
+
|
|
372
|
+
assert resp.getheader("Access-Control-Allow-Origin") == "*"
|
|
373
|
+
conn.close()
|
|
374
|
+
|
|
375
|
+
def test_api_daemon_backend_health_keys(self, dashboard_server):
|
|
376
|
+
"""/api/daemon backend_health contains all standard backend names."""
|
|
377
|
+
server, port = dashboard_server
|
|
378
|
+
conn = HTTPConnection("127.0.0.1", port, timeout=5)
|
|
379
|
+
conn.request("GET", "/api/daemon")
|
|
380
|
+
resp = conn.getresponse()
|
|
381
|
+
|
|
382
|
+
data = json.loads(resp.read())
|
|
383
|
+
bh = data.get("backend_health", {})
|
|
384
|
+
for key in ("ollama", "anthropic", "grok", "kimi", "nvidia"):
|
|
385
|
+
assert key in bh
|
|
386
|
+
conn.close()
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class TestDashboardJsonCLI:
|
|
390
|
+
"""Test the skcapstone dashboard --json CLI flag."""
|
|
391
|
+
|
|
392
|
+
def test_json_flag_outputs_valid_json(self, agent_home):
|
|
393
|
+
"""dashboard --json prints JSON to stdout and exits without starting a server."""
|
|
394
|
+
from click.testing import CliRunner
|
|
395
|
+
from skcapstone.cli.status import register_status_commands
|
|
396
|
+
import click
|
|
397
|
+
|
|
398
|
+
@click.group()
|
|
399
|
+
def _cli():
|
|
400
|
+
pass
|
|
401
|
+
|
|
402
|
+
register_status_commands(_cli)
|
|
403
|
+
runner = CliRunner()
|
|
404
|
+
result = runner.invoke(
|
|
405
|
+
_cli,
|
|
406
|
+
["dashboard", "--json", f"--home={agent_home}"],
|
|
407
|
+
catch_exceptions=False,
|
|
408
|
+
)
|
|
409
|
+
assert result.exit_code == 0, result.output
|
|
410
|
+
data = json.loads(result.output)
|
|
411
|
+
assert "daemon" in data
|
|
412
|
+
assert "consciousness" in data
|
|
413
|
+
assert "backend_health" in data
|
|
414
|
+
|
|
415
|
+
def test_json_flag_contains_generated_at(self, agent_home):
|
|
416
|
+
"""dashboard --json output includes a generated_at timestamp."""
|
|
417
|
+
from click.testing import CliRunner
|
|
418
|
+
from skcapstone.cli.status import register_status_commands
|
|
419
|
+
import click
|
|
420
|
+
|
|
421
|
+
@click.group()
|
|
422
|
+
def _cli():
|
|
423
|
+
pass
|
|
424
|
+
|
|
425
|
+
register_status_commands(_cli)
|
|
426
|
+
runner = CliRunner()
|
|
427
|
+
result = runner.invoke(
|
|
428
|
+
_cli,
|
|
429
|
+
["dashboard", "--json", f"--home={agent_home}"],
|
|
430
|
+
catch_exceptions=False,
|
|
431
|
+
)
|
|
432
|
+
data = json.loads(result.output)
|
|
433
|
+
assert "generated_at" in data
|
|
434
|
+
|
|
435
|
+
def test_json_flag_daemon_offline_still_exits_zero(self, agent_home):
|
|
436
|
+
"""dashboard --json exits 0 even when daemon is unreachable (daemon offline)."""
|
|
437
|
+
from click.testing import CliRunner
|
|
438
|
+
from skcapstone.cli.status import register_status_commands
|
|
439
|
+
import click
|
|
440
|
+
|
|
441
|
+
@click.group()
|
|
442
|
+
def _cli():
|
|
443
|
+
pass
|
|
444
|
+
|
|
445
|
+
register_status_commands(_cli)
|
|
446
|
+
runner = CliRunner()
|
|
447
|
+
result = runner.invoke(
|
|
448
|
+
_cli,
|
|
449
|
+
["dashboard", "--json", "--daemon-port=19999", f"--home={agent_home}"],
|
|
450
|
+
catch_exceptions=False,
|
|
451
|
+
)
|
|
452
|
+
assert result.exit_code == 0
|
|
453
|
+
data = json.loads(result.output)
|
|
454
|
+
assert data["daemon"]["running"] is False
|