@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,206 @@
|
|
|
1
|
+
"""Tests for the skcapstone test CLI command (test_cmd.py).
|
|
2
|
+
|
|
3
|
+
Covers:
|
|
4
|
+
- Help output is rendered correctly
|
|
5
|
+
- Invalid package names are rejected with a clear error
|
|
6
|
+
- JSON output mode produces valid structured JSON
|
|
7
|
+
- Per-package table renders for available/unavailable packages
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from unittest.mock import patch
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
from click.testing import CliRunner
|
|
18
|
+
|
|
19
|
+
from skcapstone.cli import main
|
|
20
|
+
from skcapstone.testrunner import PackageResult, TestReport
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture()
|
|
24
|
+
def runner():
|
|
25
|
+
return CliRunner(mix_stderr=False)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.fixture()
|
|
29
|
+
def mock_report_all_pass():
|
|
30
|
+
"""A TestReport where every package passes."""
|
|
31
|
+
return TestReport(
|
|
32
|
+
results=[
|
|
33
|
+
PackageResult(name="skcapstone", passed=10, exit_code=0, duration_s=1.2),
|
|
34
|
+
PackageResult(name="skcomm", passed=5, exit_code=0, duration_s=0.8),
|
|
35
|
+
],
|
|
36
|
+
duration_s=2.0,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@pytest.fixture()
|
|
41
|
+
def mock_report_with_failure():
|
|
42
|
+
"""A TestReport with one failing package."""
|
|
43
|
+
return TestReport(
|
|
44
|
+
results=[
|
|
45
|
+
PackageResult(name="skcapstone", passed=10, exit_code=0, duration_s=1.2),
|
|
46
|
+
PackageResult(
|
|
47
|
+
name="skcomm",
|
|
48
|
+
passed=3,
|
|
49
|
+
failed=2,
|
|
50
|
+
exit_code=1,
|
|
51
|
+
duration_s=0.9,
|
|
52
|
+
output="FAILED tests/test_foo.py::test_bar",
|
|
53
|
+
),
|
|
54
|
+
],
|
|
55
|
+
duration_s=2.1,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pytest.fixture()
|
|
60
|
+
def mock_report_unavailable():
|
|
61
|
+
"""A TestReport with one unavailable package."""
|
|
62
|
+
return TestReport(
|
|
63
|
+
results=[
|
|
64
|
+
PackageResult(
|
|
65
|
+
name="capauth",
|
|
66
|
+
available=False,
|
|
67
|
+
output="Test directory not found",
|
|
68
|
+
),
|
|
69
|
+
],
|
|
70
|
+
duration_s=0.0,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# ── Help output ──────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class TestTestCmdHelp:
|
|
78
|
+
"""Ensure help text is registered and contains key information."""
|
|
79
|
+
|
|
80
|
+
def test_help_renders(self, runner):
|
|
81
|
+
"""--help exits 0 and shows package names."""
|
|
82
|
+
result = runner.invoke(main, ["test", "--help"])
|
|
83
|
+
assert result.exit_code == 0
|
|
84
|
+
assert "skcapstone" in result.output
|
|
85
|
+
assert "--package" in result.output
|
|
86
|
+
|
|
87
|
+
def test_help_lists_options(self, runner):
|
|
88
|
+
"""--help shows all major options."""
|
|
89
|
+
result = runner.invoke(main, ["test", "--help"])
|
|
90
|
+
assert "--fast" in result.output
|
|
91
|
+
assert "--verbose" in result.output
|
|
92
|
+
assert "--json-out" in result.output
|
|
93
|
+
assert "--timeout" in result.output
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# ── Invalid package validation ───────────────────────────────────
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class TestInvalidPackage:
|
|
100
|
+
"""Unknown package names should be rejected immediately."""
|
|
101
|
+
|
|
102
|
+
def test_invalid_package_exits_nonzero(self, runner):
|
|
103
|
+
"""Passing an unknown --package name exits with code 1."""
|
|
104
|
+
result = runner.invoke(main, ["test", "--package", "does-not-exist"])
|
|
105
|
+
assert result.exit_code == 1
|
|
106
|
+
|
|
107
|
+
def test_invalid_package_shows_valid_names(self, runner):
|
|
108
|
+
"""Error message lists the valid package names."""
|
|
109
|
+
result = runner.invoke(main, ["test", "--package", "bogus"])
|
|
110
|
+
assert "bogus" in result.output
|
|
111
|
+
assert "skcapstone" in result.output
|
|
112
|
+
|
|
113
|
+
def test_valid_package_accepted(self, runner, mock_report_all_pass):
|
|
114
|
+
"""A valid package name is accepted and run_all_tests is called."""
|
|
115
|
+
with patch(
|
|
116
|
+
"skcapstone.cli.test_cmd.run_all_tests",
|
|
117
|
+
return_value=mock_report_all_pass,
|
|
118
|
+
):
|
|
119
|
+
result = runner.invoke(main, ["test", "--package", "skcomm"])
|
|
120
|
+
assert result.exit_code == 0
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# ── JSON output mode ─────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class TestJsonOutput:
|
|
127
|
+
"""--json-out should produce parseable JSON with the right structure."""
|
|
128
|
+
|
|
129
|
+
def test_json_out_is_parseable(self, runner, mock_report_all_pass):
|
|
130
|
+
"""JSON output can be loaded by json.loads."""
|
|
131
|
+
with patch(
|
|
132
|
+
"skcapstone.cli.test_cmd.run_all_tests",
|
|
133
|
+
return_value=mock_report_all_pass,
|
|
134
|
+
):
|
|
135
|
+
result = runner.invoke(main, ["test", "--json-out"])
|
|
136
|
+
assert result.exit_code == 0
|
|
137
|
+
data = json.loads(result.output)
|
|
138
|
+
assert "all_passed" in data
|
|
139
|
+
assert "packages" in data
|
|
140
|
+
|
|
141
|
+
def test_json_exit_nonzero_on_failure(self, runner, mock_report_with_failure):
|
|
142
|
+
"""JSON mode still exits 1 when tests fail."""
|
|
143
|
+
with patch(
|
|
144
|
+
"skcapstone.cli.test_cmd.run_all_tests",
|
|
145
|
+
return_value=mock_report_with_failure,
|
|
146
|
+
):
|
|
147
|
+
result = runner.invoke(main, ["test", "--json-out"])
|
|
148
|
+
assert result.exit_code == 1
|
|
149
|
+
data = json.loads(result.output)
|
|
150
|
+
assert data["all_passed"] is False
|
|
151
|
+
|
|
152
|
+
def test_json_has_package_details(self, runner, mock_report_all_pass):
|
|
153
|
+
"""Each package result appears in the packages array."""
|
|
154
|
+
with patch(
|
|
155
|
+
"skcapstone.cli.test_cmd.run_all_tests",
|
|
156
|
+
return_value=mock_report_all_pass,
|
|
157
|
+
):
|
|
158
|
+
result = runner.invoke(main, ["test", "--json-out"])
|
|
159
|
+
data = json.loads(result.output)
|
|
160
|
+
pkg_names = [p["name"] for p in data["packages"]]
|
|
161
|
+
assert "skcapstone" in pkg_names
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# ── Table rendering ──────────────────────────────────────────────
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class TestTableRendering:
|
|
168
|
+
"""Rich table should contain pass/fail/skip columns."""
|
|
169
|
+
|
|
170
|
+
def test_pass_table_shows_pass(self, runner, mock_report_all_pass):
|
|
171
|
+
"""PASS appears in the output when all tests succeed."""
|
|
172
|
+
with patch(
|
|
173
|
+
"skcapstone.cli.test_cmd.run_all_tests",
|
|
174
|
+
return_value=mock_report_all_pass,
|
|
175
|
+
):
|
|
176
|
+
result = runner.invoke(main, ["test"])
|
|
177
|
+
assert result.exit_code == 0
|
|
178
|
+
assert "PASS" in result.output
|
|
179
|
+
|
|
180
|
+
def test_fail_table_shows_fail(self, runner, mock_report_with_failure):
|
|
181
|
+
"""FAIL appears in the output when a package fails."""
|
|
182
|
+
with patch(
|
|
183
|
+
"skcapstone.cli.test_cmd.run_all_tests",
|
|
184
|
+
return_value=mock_report_with_failure,
|
|
185
|
+
):
|
|
186
|
+
result = runner.invoke(main, ["test"])
|
|
187
|
+
assert result.exit_code == 1
|
|
188
|
+
assert "FAIL" in result.output
|
|
189
|
+
|
|
190
|
+
def test_unavailable_package_shows_na(self, runner, mock_report_unavailable):
|
|
191
|
+
"""Packages without test dirs show N/A, not an error."""
|
|
192
|
+
with patch(
|
|
193
|
+
"skcapstone.cli.test_cmd.run_all_tests",
|
|
194
|
+
return_value=mock_report_unavailable,
|
|
195
|
+
):
|
|
196
|
+
result = runner.invoke(main, ["test"])
|
|
197
|
+
assert "N/A" in result.output
|
|
198
|
+
|
|
199
|
+
def test_show_output_flag_prints_failures(self, runner, mock_report_with_failure):
|
|
200
|
+
"""--show-output flag prints pytest output for failing packages."""
|
|
201
|
+
with patch(
|
|
202
|
+
"skcapstone.cli.test_cmd.run_all_tests",
|
|
203
|
+
return_value=mock_report_with_failure,
|
|
204
|
+
):
|
|
205
|
+
result = runner.invoke(main, ["test", "--show-output"])
|
|
206
|
+
assert "test_bar" in result.output
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
"""Tests for skcapstone test-connection command.
|
|
2
|
+
|
|
3
|
+
Covers:
|
|
4
|
+
- Happy path: peer responds with pong, latency reported
|
|
5
|
+
- Timeout: peer never replies, command exits with code 1
|
|
6
|
+
- No-transport: SKComm has no live transport, error reported
|
|
7
|
+
- Payload helpers: _make_ping_payload / _is_pong_for / _is_ping / _make_pong_payload
|
|
8
|
+
- --count > 1: multi-ping summary statistics
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import time
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from unittest.mock import MagicMock, patch
|
|
17
|
+
|
|
18
|
+
import pytest
|
|
19
|
+
from click.testing import CliRunner
|
|
20
|
+
|
|
21
|
+
from skcapstone.cli.test_connection import (
|
|
22
|
+
_is_ping,
|
|
23
|
+
_is_pong_for,
|
|
24
|
+
_make_ping_payload,
|
|
25
|
+
_make_pong_payload,
|
|
26
|
+
ping_peer,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
# Fixtures
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.fixture
|
|
36
|
+
def agent_home(tmp_path):
|
|
37
|
+
"""Minimal agent home directory for CLI tests."""
|
|
38
|
+
home = tmp_path / ".skcapstone"
|
|
39
|
+
(home / "identity").mkdir(parents=True)
|
|
40
|
+
(home / "config").mkdir(parents=True)
|
|
41
|
+
identity = {
|
|
42
|
+
"name": "TestAgent",
|
|
43
|
+
"fingerprint": "AABB1234",
|
|
44
|
+
"capauth_managed": False,
|
|
45
|
+
}
|
|
46
|
+
(home / "identity" / "identity.json").write_text(json.dumps(identity))
|
|
47
|
+
(home / "manifest.json").write_text(
|
|
48
|
+
json.dumps({"name": "TestAgent", "version": "0.1.0"})
|
|
49
|
+
)
|
|
50
|
+
import yaml
|
|
51
|
+
|
|
52
|
+
(home / "config" / "config.yaml").write_text(
|
|
53
|
+
yaml.dump({"agent_name": "TestAgent"})
|
|
54
|
+
)
|
|
55
|
+
return home
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _make_mock_chat(delivered: bool, pong_nonce: str | None = None, peer: str = "lumina"):
|
|
59
|
+
"""Build a mocked AgentChat instance.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
delivered: Whether AgentChat.send() reports successful delivery.
|
|
63
|
+
pong_nonce: When set, AgentChat.receive() will return a pong message
|
|
64
|
+
matching this nonce on the first call.
|
|
65
|
+
peer: Peer name used as sender in the pong message.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
MagicMock configured as AgentChat.
|
|
69
|
+
"""
|
|
70
|
+
mock_chat = MagicMock()
|
|
71
|
+
mock_chat.send.return_value = {
|
|
72
|
+
"stored": True,
|
|
73
|
+
"delivered": delivered,
|
|
74
|
+
"transport": "tailscale" if delivered else None,
|
|
75
|
+
"error": None,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if pong_nonce is not None:
|
|
79
|
+
pong_payload = _make_pong_payload(pong_nonce, peer)
|
|
80
|
+
mock_chat.receive.return_value = [
|
|
81
|
+
{"sender": peer, "content": pong_payload}
|
|
82
|
+
]
|
|
83
|
+
else:
|
|
84
|
+
mock_chat.receive.return_value = []
|
|
85
|
+
|
|
86
|
+
return mock_chat
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# ---------------------------------------------------------------------------
|
|
90
|
+
# Unit tests — payload helpers
|
|
91
|
+
# ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class TestPayloadHelpers:
|
|
95
|
+
"""Verify ping/pong serialization round-trips."""
|
|
96
|
+
|
|
97
|
+
def test_make_ping_payload_is_json(self):
|
|
98
|
+
"""Ping payload is valid JSON with required keys."""
|
|
99
|
+
payload = _make_ping_payload("abc123", "opus")
|
|
100
|
+
data = json.loads(payload)
|
|
101
|
+
assert data["skchat_ping"] is True
|
|
102
|
+
assert data["nonce"] == "abc123"
|
|
103
|
+
assert data["sender"] == "opus"
|
|
104
|
+
|
|
105
|
+
def test_is_pong_for_matching_nonce(self):
|
|
106
|
+
"""_is_pong_for returns True for a matching pong."""
|
|
107
|
+
pong = _make_pong_payload("xyz-789", "lumina")
|
|
108
|
+
assert _is_pong_for(pong, "xyz-789") is True
|
|
109
|
+
|
|
110
|
+
def test_is_pong_for_wrong_nonce(self):
|
|
111
|
+
"""_is_pong_for returns False when nonce differs."""
|
|
112
|
+
pong = _make_pong_payload("xyz-789", "lumina")
|
|
113
|
+
assert _is_pong_for(pong, "different-nonce") is False
|
|
114
|
+
|
|
115
|
+
def test_is_pong_for_plain_text(self):
|
|
116
|
+
"""_is_pong_for is False for arbitrary text."""
|
|
117
|
+
assert _is_pong_for("hello world", "nonce") is False
|
|
118
|
+
|
|
119
|
+
def test_is_ping_detects_ping(self):
|
|
120
|
+
"""_is_ping correctly identifies a ping payload."""
|
|
121
|
+
ping = _make_ping_payload("nonce-42", "opus")
|
|
122
|
+
ok, nonce, sender = _is_ping(ping)
|
|
123
|
+
assert ok is True
|
|
124
|
+
assert nonce == "nonce-42"
|
|
125
|
+
assert sender == "opus"
|
|
126
|
+
|
|
127
|
+
def test_is_ping_plain_text(self):
|
|
128
|
+
"""_is_ping returns False for non-ping content."""
|
|
129
|
+
ok, nonce, sender = _is_ping("just a regular message")
|
|
130
|
+
assert ok is False
|
|
131
|
+
assert nonce == ""
|
|
132
|
+
|
|
133
|
+
def test_make_pong_payload_round_trip(self):
|
|
134
|
+
"""Pong payload can be verified by _is_pong_for."""
|
|
135
|
+
pong = _make_pong_payload("round-trip-nonce", "jarvis")
|
|
136
|
+
assert _is_pong_for(pong, "round-trip-nonce") is True
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# ---------------------------------------------------------------------------
|
|
140
|
+
# Unit tests — ping_peer()
|
|
141
|
+
# ---------------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class TestPingPeer:
|
|
145
|
+
"""Tests for the ping_peer() core function."""
|
|
146
|
+
|
|
147
|
+
def test_happy_path_reachable(self, agent_home):
|
|
148
|
+
"""ping_peer returns reachable=True when pong arrives."""
|
|
149
|
+
nonce_holder: list[str] = []
|
|
150
|
+
|
|
151
|
+
def fake_send(peer, message, **kwargs):
|
|
152
|
+
# Capture the nonce from the ping payload
|
|
153
|
+
data = json.loads(message)
|
|
154
|
+
nonce_holder.append(data["nonce"])
|
|
155
|
+
return {"stored": True, "delivered": True, "transport": "tailscale", "error": None}
|
|
156
|
+
|
|
157
|
+
def fake_receive(limit=20):
|
|
158
|
+
if not nonce_holder:
|
|
159
|
+
return []
|
|
160
|
+
return [
|
|
161
|
+
{
|
|
162
|
+
"sender": "lumina",
|
|
163
|
+
"content": _make_pong_payload(nonce_holder[0], "lumina"),
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
mock_chat = MagicMock()
|
|
168
|
+
mock_chat.send.side_effect = fake_send
|
|
169
|
+
mock_chat.receive.side_effect = fake_receive
|
|
170
|
+
|
|
171
|
+
with patch(
|
|
172
|
+
"skcapstone.cli.test_connection.AgentChat", return_value=mock_chat
|
|
173
|
+
):
|
|
174
|
+
result = ping_peer("lumina", agent_home, "TestAgent", timeout=2.0)
|
|
175
|
+
|
|
176
|
+
assert result["reachable"] is True
|
|
177
|
+
assert result["latency_ms"] is not None
|
|
178
|
+
assert result["latency_ms"] >= 0.0
|
|
179
|
+
|
|
180
|
+
def test_timeout_unreachable(self, agent_home):
|
|
181
|
+
"""ping_peer returns reachable=False when pong never arrives."""
|
|
182
|
+
mock_chat = _make_mock_chat(delivered=True, pong_nonce=None)
|
|
183
|
+
|
|
184
|
+
with patch(
|
|
185
|
+
"skcapstone.cli.test_connection.AgentChat", return_value=mock_chat
|
|
186
|
+
):
|
|
187
|
+
# Use a very short timeout so the test runs quickly
|
|
188
|
+
result = ping_peer("lumina", agent_home, "TestAgent", timeout=0.3)
|
|
189
|
+
|
|
190
|
+
assert result["reachable"] is False
|
|
191
|
+
assert result["latency_ms"] is None
|
|
192
|
+
assert "timeout" in (result["error"] or "").lower()
|
|
193
|
+
|
|
194
|
+
def test_no_live_transport_returns_error(self, agent_home):
|
|
195
|
+
"""ping_peer reports error when send has no live transport."""
|
|
196
|
+
mock_chat = MagicMock()
|
|
197
|
+
mock_chat.send.return_value = {
|
|
198
|
+
"stored": True,
|
|
199
|
+
"delivered": False,
|
|
200
|
+
"transport": None,
|
|
201
|
+
"error": None,
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
with patch(
|
|
205
|
+
"skcapstone.cli.test_connection.AgentChat", return_value=mock_chat
|
|
206
|
+
):
|
|
207
|
+
result = ping_peer("lumina", agent_home, "TestAgent", timeout=2.0)
|
|
208
|
+
|
|
209
|
+
assert result["reachable"] is False
|
|
210
|
+
assert "transport" in (result["error"] or "").lower()
|
|
211
|
+
|
|
212
|
+
def test_send_failure_returns_error(self, agent_home):
|
|
213
|
+
"""ping_peer returns error when send itself fails entirely."""
|
|
214
|
+
mock_chat = MagicMock()
|
|
215
|
+
mock_chat.send.return_value = {
|
|
216
|
+
"stored": False,
|
|
217
|
+
"delivered": False,
|
|
218
|
+
"transport": None,
|
|
219
|
+
"error": "connection refused",
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
with patch(
|
|
223
|
+
"skcapstone.cli.test_connection.AgentChat", return_value=mock_chat
|
|
224
|
+
):
|
|
225
|
+
result = ping_peer("lumina", agent_home, "TestAgent", timeout=2.0)
|
|
226
|
+
|
|
227
|
+
assert result["reachable"] is False
|
|
228
|
+
assert result["error"] is not None
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# ---------------------------------------------------------------------------
|
|
232
|
+
# CLI integration tests — skcapstone test-connection
|
|
233
|
+
# ---------------------------------------------------------------------------
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class TestTestConnectionCLI:
|
|
237
|
+
"""CLI-level tests via CliRunner."""
|
|
238
|
+
|
|
239
|
+
def _run(self, args: list[str]):
|
|
240
|
+
from skcapstone.cli import main
|
|
241
|
+
|
|
242
|
+
runner = CliRunner()
|
|
243
|
+
return runner.invoke(main, args, catch_exceptions=False)
|
|
244
|
+
|
|
245
|
+
def test_cli_reachable_exit_zero(self, agent_home):
|
|
246
|
+
"""CLI exits 0 and prints REACHABLE when pong arrives."""
|
|
247
|
+
nonce_holder: list[str] = []
|
|
248
|
+
|
|
249
|
+
def fake_send(peer, message, **kwargs):
|
|
250
|
+
try:
|
|
251
|
+
data = json.loads(message)
|
|
252
|
+
nonce_holder.append(data.get("nonce", ""))
|
|
253
|
+
except Exception:
|
|
254
|
+
pass
|
|
255
|
+
return {
|
|
256
|
+
"stored": True,
|
|
257
|
+
"delivered": True,
|
|
258
|
+
"transport": "tailscale",
|
|
259
|
+
"error": None,
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
def fake_receive(limit=20):
|
|
263
|
+
if not nonce_holder:
|
|
264
|
+
return []
|
|
265
|
+
return [
|
|
266
|
+
{
|
|
267
|
+
"sender": "lumina",
|
|
268
|
+
"content": _make_pong_payload(nonce_holder[0], "lumina"),
|
|
269
|
+
}
|
|
270
|
+
]
|
|
271
|
+
|
|
272
|
+
mock_chat = MagicMock()
|
|
273
|
+
mock_chat.send.side_effect = fake_send
|
|
274
|
+
mock_chat.receive.side_effect = fake_receive
|
|
275
|
+
|
|
276
|
+
with patch("skcapstone.cli.test_connection.AgentChat", return_value=mock_chat):
|
|
277
|
+
result = self._run(
|
|
278
|
+
["test-connection", "lumina", "--home", str(agent_home)]
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
assert result.exit_code == 0, result.output
|
|
282
|
+
assert "REACHABLE" in result.output
|
|
283
|
+
|
|
284
|
+
def test_cli_timeout_exit_one(self, agent_home):
|
|
285
|
+
"""CLI exits 1 and prints UNREACHABLE on timeout."""
|
|
286
|
+
mock_chat = _make_mock_chat(delivered=True, pong_nonce=None)
|
|
287
|
+
|
|
288
|
+
with patch("skcapstone.cli.test_connection.AgentChat", return_value=mock_chat):
|
|
289
|
+
# Very short timeout so the test completes fast
|
|
290
|
+
result = self._run(
|
|
291
|
+
[
|
|
292
|
+
"test-connection", "lumina",
|
|
293
|
+
"--home", str(agent_home),
|
|
294
|
+
"--timeout", "0.3",
|
|
295
|
+
]
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
assert result.exit_code == 1, result.output
|
|
299
|
+
assert "UNREACHABLE" in result.output
|
|
300
|
+
|
|
301
|
+
def test_cli_no_transport_exit_one(self, agent_home):
|
|
302
|
+
"""CLI exits 1 when no live transport is available."""
|
|
303
|
+
mock_chat = MagicMock()
|
|
304
|
+
mock_chat.send.return_value = {
|
|
305
|
+
"stored": True,
|
|
306
|
+
"delivered": False,
|
|
307
|
+
"transport": None,
|
|
308
|
+
"error": None,
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
with patch("skcapstone.cli.test_connection.AgentChat", return_value=mock_chat):
|
|
312
|
+
result = self._run(
|
|
313
|
+
["test-connection", "lumina", "--home", str(agent_home)]
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
assert result.exit_code == 1, result.output
|
|
317
|
+
assert "UNREACHABLE" in result.output
|
|
318
|
+
|
|
319
|
+
def test_cli_count_shows_statistics(self, agent_home):
|
|
320
|
+
"""--count 3 produces min/avg/max latency summary."""
|
|
321
|
+
nonce_holder: list[str] = []
|
|
322
|
+
|
|
323
|
+
def fake_send(peer, message, **kwargs):
|
|
324
|
+
try:
|
|
325
|
+
data = json.loads(message)
|
|
326
|
+
nonce_holder.append(data.get("nonce", ""))
|
|
327
|
+
except Exception:
|
|
328
|
+
pass
|
|
329
|
+
return {
|
|
330
|
+
"stored": True,
|
|
331
|
+
"delivered": True,
|
|
332
|
+
"transport": "tailscale",
|
|
333
|
+
"error": None,
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
def fake_receive(limit=20):
|
|
337
|
+
if not nonce_holder:
|
|
338
|
+
return []
|
|
339
|
+
latest = nonce_holder[-1]
|
|
340
|
+
return [
|
|
341
|
+
{
|
|
342
|
+
"sender": "lumina",
|
|
343
|
+
"content": _make_pong_payload(latest, "lumina"),
|
|
344
|
+
}
|
|
345
|
+
]
|
|
346
|
+
|
|
347
|
+
mock_chat = MagicMock()
|
|
348
|
+
mock_chat.send.side_effect = fake_send
|
|
349
|
+
mock_chat.receive.side_effect = fake_receive
|
|
350
|
+
|
|
351
|
+
with patch("skcapstone.cli.test_connection.AgentChat", return_value=mock_chat):
|
|
352
|
+
result = self._run(
|
|
353
|
+
[
|
|
354
|
+
"test-connection", "lumina",
|
|
355
|
+
"--home", str(agent_home),
|
|
356
|
+
"--count", "3",
|
|
357
|
+
]
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
assert result.exit_code == 0, result.output
|
|
361
|
+
# Summary line for multiple pings contains avg=
|
|
362
|
+
assert "avg=" in result.output
|
|
363
|
+
assert "min=" in result.output
|
|
364
|
+
assert "max=" in result.output
|