@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,144 @@
|
|
|
1
|
+
"""Backup and restore commands: create, restore, list."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from ._common import AGENT_HOME, console
|
|
10
|
+
from ._validators import validate_file_path
|
|
11
|
+
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
from rich.table import Table
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def register_backup_commands(main: click.Group) -> None:
|
|
17
|
+
"""Register the backup command group."""
|
|
18
|
+
|
|
19
|
+
@main.group()
|
|
20
|
+
def backup():
|
|
21
|
+
"""Backup and restore — portable sovereign agent state.
|
|
22
|
+
|
|
23
|
+
Create encrypted backups of your full agent state and
|
|
24
|
+
restore on any machine. Your identity travels with you.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
@backup.command("create")
|
|
28
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path(), help="Agent home directory.")
|
|
29
|
+
@click.option("--output", "-o", default=None, type=click.Path(), help="Output directory.")
|
|
30
|
+
def backup_create(home: str, output: str):
|
|
31
|
+
"""Create a full backup of the sovereign agent state.
|
|
32
|
+
|
|
33
|
+
Archives identity, memories, trust, config, coordination,
|
|
34
|
+
and agent card into a compressed tarball with integrity checksums.
|
|
35
|
+
|
|
36
|
+
Examples:
|
|
37
|
+
|
|
38
|
+
skcapstone backup create
|
|
39
|
+
|
|
40
|
+
skcapstone backup create -o /mnt/usb/backups
|
|
41
|
+
"""
|
|
42
|
+
from ..backup import create_backup
|
|
43
|
+
|
|
44
|
+
home_path = Path(home).expanduser()
|
|
45
|
+
out_dir = Path(output).expanduser() if output else None
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
console.print("\n[cyan]Creating backup...[/]")
|
|
49
|
+
result = create_backup(home=home_path, output_dir=out_dir)
|
|
50
|
+
|
|
51
|
+
size_mb = result["archive_size"] / 1024 / 1024
|
|
52
|
+
console.print(Panel(
|
|
53
|
+
f"[bold green]Backup created[/]\n"
|
|
54
|
+
f"ID: {result['backup_id']}\n"
|
|
55
|
+
f"Files: {result['file_count']}\n"
|
|
56
|
+
f"Size: {size_mb:.1f} MB\n"
|
|
57
|
+
f"Path: [cyan]{result['filepath']}[/]",
|
|
58
|
+
title="Backup Complete",
|
|
59
|
+
border_style="green",
|
|
60
|
+
))
|
|
61
|
+
except FileNotFoundError as exc:
|
|
62
|
+
console.print(f"[red]{exc}[/]")
|
|
63
|
+
raise SystemExit(1)
|
|
64
|
+
|
|
65
|
+
@backup.command("restore")
|
|
66
|
+
@click.argument("archive")
|
|
67
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path(), help="Target home directory.")
|
|
68
|
+
@click.option("--no-verify", is_flag=True, help="Skip checksum verification.")
|
|
69
|
+
def backup_restore(archive: str, home: str, no_verify: bool):
|
|
70
|
+
"""Restore the agent from a backup archive.
|
|
71
|
+
|
|
72
|
+
Extracts the backup and verifies file integrity.
|
|
73
|
+
|
|
74
|
+
Examples:
|
|
75
|
+
|
|
76
|
+
skcapstone backup restore backup-20260224.tar.gz
|
|
77
|
+
|
|
78
|
+
skcapstone backup restore /mnt/usb/backup.tar.gz --home ~/.skcapstone-new
|
|
79
|
+
"""
|
|
80
|
+
from ..backup import restore_backup
|
|
81
|
+
|
|
82
|
+
validate_file_path(archive)
|
|
83
|
+
|
|
84
|
+
target = Path(home).expanduser()
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
console.print(f"\n[cyan]Restoring from {archive}...[/]")
|
|
88
|
+
result = restore_backup(
|
|
89
|
+
archive_path=archive,
|
|
90
|
+
target_home=target,
|
|
91
|
+
verify=not no_verify,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
status = "[green]VERIFIED[/]" if result["verified"] else "[red]ERRORS[/]"
|
|
95
|
+
console.print(Panel(
|
|
96
|
+
f"[bold green]Restore complete[/]\n"
|
|
97
|
+
f"Agent: {result['agent_name']}\n"
|
|
98
|
+
f"Files: {result['file_count']}\n"
|
|
99
|
+
f"Target: [cyan]{result['target']}[/]\n"
|
|
100
|
+
f"Integrity: {status}",
|
|
101
|
+
title="Restore Complete",
|
|
102
|
+
border_style="green",
|
|
103
|
+
))
|
|
104
|
+
|
|
105
|
+
if result["errors"]:
|
|
106
|
+
console.print("[yellow]Verification errors:[/]")
|
|
107
|
+
for err in result["errors"]:
|
|
108
|
+
console.print(f" [red]{err}[/]")
|
|
109
|
+
except (FileNotFoundError, ValueError) as exc:
|
|
110
|
+
console.print(f"[red]{exc}[/]")
|
|
111
|
+
raise SystemExit(1)
|
|
112
|
+
|
|
113
|
+
@backup.command("list")
|
|
114
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path(), help="Agent home directory.")
|
|
115
|
+
def backup_list(home: str):
|
|
116
|
+
"""List available backups.
|
|
117
|
+
|
|
118
|
+
Examples:
|
|
119
|
+
|
|
120
|
+
skcapstone backup list
|
|
121
|
+
"""
|
|
122
|
+
from ..backup import list_backups
|
|
123
|
+
|
|
124
|
+
home_path = Path(home).expanduser()
|
|
125
|
+
backups = list_backups(home_path / "backups")
|
|
126
|
+
|
|
127
|
+
if not backups:
|
|
128
|
+
console.print("\n[dim]No backups found.[/]\n")
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
132
|
+
table.add_column("Filename", style="cyan")
|
|
133
|
+
table.add_column("Size", justify="right")
|
|
134
|
+
table.add_column("Created", style="dim")
|
|
135
|
+
|
|
136
|
+
for b in backups:
|
|
137
|
+
size_mb = b["size"] / 1024 / 1024
|
|
138
|
+
table.add_row(b["filename"], f"{size_mb:.1f} MB", b["created"][:19])
|
|
139
|
+
|
|
140
|
+
console.print(f"\n[bold]{len(backups)}[/] backup(s):\n")
|
|
141
|
+
console.print(table)
|
|
142
|
+
console.print()
|
|
143
|
+
|
|
144
|
+
main.add_command(backup)
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"""Benchmark SKComm transport throughput and latency.
|
|
2
|
+
|
|
3
|
+
Sends N messages of a configurable size through each available
|
|
4
|
+
SKComm transport. Reports p50/p95/p99 latency, throughput (msg/s),
|
|
5
|
+
and error rate.
|
|
6
|
+
|
|
7
|
+
The file transport is benchmarked via a local temp-dir loopback
|
|
8
|
+
(always available). Network transports (syncthing, nostr, websocket,
|
|
9
|
+
tailscale, webrtc) are probed with repeated health_check() calls
|
|
10
|
+
when available.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import importlib
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import shutil
|
|
19
|
+
import tempfile
|
|
20
|
+
import time
|
|
21
|
+
from typing import Optional
|
|
22
|
+
|
|
23
|
+
import click
|
|
24
|
+
from rich.table import Table
|
|
25
|
+
|
|
26
|
+
from ._common import console
|
|
27
|
+
|
|
28
|
+
# Mirrors skcomm.core.BUILTIN_TRANSPORTS — kept local to avoid hard dep
|
|
29
|
+
_BUILTIN_TRANSPORTS: dict[str, str] = {
|
|
30
|
+
"file": "skcomm.transports.file",
|
|
31
|
+
"syncthing": "skcomm.transports.syncthing",
|
|
32
|
+
"nostr": "skcomm.transports.nostr",
|
|
33
|
+
"websocket": "skcomm.transports.websocket",
|
|
34
|
+
"tailscale": "skcomm.transports.tailscale",
|
|
35
|
+
"webrtc": "skcomm.transports.webrtc",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
# Stats helpers
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _percentile(data: list[float], p: float) -> Optional[float]:
|
|
45
|
+
"""Return the p-th percentile of *data*, or None if data is empty."""
|
|
46
|
+
if not data:
|
|
47
|
+
return None
|
|
48
|
+
s = sorted(data)
|
|
49
|
+
idx = min(int(len(s) * p / 100), len(s) - 1)
|
|
50
|
+
return round(s[idx], 3)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
# Per-transport benchmark runners
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _bench_file_loopback(count: int, size: int) -> dict:
|
|
59
|
+
"""Benchmark the file transport via a temp-dir send-loop loopback.
|
|
60
|
+
|
|
61
|
+
Creates a temporary directory, configures the file transport to use
|
|
62
|
+
it as both outbox and inbox, sends *count* messages of *size* bytes,
|
|
63
|
+
and measures per-send latency from the returned SendResult.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
count: Number of messages to send.
|
|
67
|
+
size: Payload size in bytes.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Result dict with status, mode, latency percentiles, throughput,
|
|
71
|
+
and error counts.
|
|
72
|
+
"""
|
|
73
|
+
tmp = tempfile.mkdtemp(prefix="skcomm_bench_")
|
|
74
|
+
try:
|
|
75
|
+
mod = importlib.import_module("skcomm.transports.file")
|
|
76
|
+
factory = getattr(mod, "create_transport", None)
|
|
77
|
+
if factory is None:
|
|
78
|
+
return {"status": "error", "error": "no create_transport() in file transport"}
|
|
79
|
+
|
|
80
|
+
transport = factory(outbox_path=tmp, inbox_path=tmp, archive=False)
|
|
81
|
+
payload = os.urandom(size)
|
|
82
|
+
latencies: list[float] = []
|
|
83
|
+
errors = 0
|
|
84
|
+
|
|
85
|
+
t_start = time.monotonic()
|
|
86
|
+
for _ in range(count):
|
|
87
|
+
try:
|
|
88
|
+
result = transport.send(payload, "bench-self")
|
|
89
|
+
latencies.append(result.latency_ms)
|
|
90
|
+
if not result.success:
|
|
91
|
+
errors += 1
|
|
92
|
+
except Exception:
|
|
93
|
+
errors += 1
|
|
94
|
+
|
|
95
|
+
total_s = time.monotonic() - t_start
|
|
96
|
+
throughput = count / total_s if total_s > 0 else 0.0
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
"status": "ok",
|
|
100
|
+
"mode": "send-loop",
|
|
101
|
+
"count": count,
|
|
102
|
+
"errors": errors,
|
|
103
|
+
"error_rate": errors / count,
|
|
104
|
+
"throughput_msg_s": round(throughput, 1),
|
|
105
|
+
"p50_ms": _percentile(latencies, 50),
|
|
106
|
+
"p95_ms": _percentile(latencies, 95),
|
|
107
|
+
"p99_ms": _percentile(latencies, 99),
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
except ImportError:
|
|
111
|
+
return {"status": "unavailable", "error": "skcomm not installed"}
|
|
112
|
+
except Exception as exc:
|
|
113
|
+
return {"status": "error", "error": str(exc)[:120]}
|
|
114
|
+
finally:
|
|
115
|
+
shutil.rmtree(tmp, ignore_errors=True)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _bench_health_checks(transport, count: int) -> dict:
|
|
119
|
+
"""Benchmark a transport by calling health_check() *count* times.
|
|
120
|
+
|
|
121
|
+
Used for network transports where a local loopback send is not
|
|
122
|
+
possible without a real peer. The health_check() latency_ms is
|
|
123
|
+
collected as a proxy for round-trip time.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
transport: A configured Transport instance with is_available()==True.
|
|
127
|
+
count: Number of health_check iterations.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
Result dict with status, mode, latency percentiles, throughput,
|
|
131
|
+
and error counts.
|
|
132
|
+
"""
|
|
133
|
+
latencies: list[float] = []
|
|
134
|
+
errors = 0
|
|
135
|
+
|
|
136
|
+
t_start = time.monotonic()
|
|
137
|
+
for _ in range(count):
|
|
138
|
+
try:
|
|
139
|
+
status = transport.health_check()
|
|
140
|
+
if status.latency_ms is not None:
|
|
141
|
+
latencies.append(float(status.latency_ms))
|
|
142
|
+
if str(status.status) != "available":
|
|
143
|
+
errors += 1
|
|
144
|
+
except Exception:
|
|
145
|
+
errors += 1
|
|
146
|
+
|
|
147
|
+
total_s = time.monotonic() - t_start
|
|
148
|
+
throughput = count / total_s if total_s > 0 else 0.0
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
"status": "ok",
|
|
152
|
+
"mode": "health-check",
|
|
153
|
+
"count": count,
|
|
154
|
+
"errors": errors,
|
|
155
|
+
"error_rate": errors / count,
|
|
156
|
+
"throughput_msg_s": round(throughput, 1),
|
|
157
|
+
"p50_ms": _percentile(latencies, 50),
|
|
158
|
+
"p95_ms": _percentile(latencies, 95),
|
|
159
|
+
"p99_ms": _percentile(latencies, 99),
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# ---------------------------------------------------------------------------
|
|
164
|
+
# Main benchmark orchestrator
|
|
165
|
+
# ---------------------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def run_bench(
|
|
169
|
+
transports: list[str],
|
|
170
|
+
count: int,
|
|
171
|
+
size: int,
|
|
172
|
+
health_count: int,
|
|
173
|
+
) -> list[dict]:
|
|
174
|
+
"""Run benchmarks for all requested transports.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
transports: Transport names to benchmark. Empty list = all.
|
|
178
|
+
count: Number of messages for the file send-loop.
|
|
179
|
+
size: Message payload size in bytes.
|
|
180
|
+
health_count: Number of health_check() iterations for network transports.
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
List of result dicts, one per transport.
|
|
184
|
+
"""
|
|
185
|
+
selected = {
|
|
186
|
+
name: path
|
|
187
|
+
for name, path in _BUILTIN_TRANSPORTS.items()
|
|
188
|
+
if not transports or name in transports
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
results: list[dict] = []
|
|
192
|
+
|
|
193
|
+
for name, module_path in selected.items():
|
|
194
|
+
r: dict = {"transport": name}
|
|
195
|
+
|
|
196
|
+
# File transport — always benchmarked via send-loop loopback
|
|
197
|
+
if name == "file":
|
|
198
|
+
r.update(_bench_file_loopback(count, size))
|
|
199
|
+
results.append(r)
|
|
200
|
+
continue
|
|
201
|
+
|
|
202
|
+
# Network transports — load module, check availability, health-check bench
|
|
203
|
+
try:
|
|
204
|
+
mod = importlib.import_module(module_path)
|
|
205
|
+
factory = getattr(mod, "create_transport", None)
|
|
206
|
+
if factory is None:
|
|
207
|
+
r.update({"status": "unavailable", "error": f"no create_transport() in {module_path}"})
|
|
208
|
+
results.append(r)
|
|
209
|
+
continue
|
|
210
|
+
except ImportError as exc:
|
|
211
|
+
r.update({"status": "unavailable", "error": f"ImportError: {exc}"})
|
|
212
|
+
results.append(r)
|
|
213
|
+
continue
|
|
214
|
+
except Exception as exc:
|
|
215
|
+
r.update({"status": "unavailable", "error": str(exc)[:80]})
|
|
216
|
+
results.append(r)
|
|
217
|
+
continue
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
transport = factory()
|
|
221
|
+
except Exception as exc:
|
|
222
|
+
r.update({"status": "unavailable", "error": f"init failed: {exc}"})
|
|
223
|
+
results.append(r)
|
|
224
|
+
continue
|
|
225
|
+
|
|
226
|
+
try:
|
|
227
|
+
available = transport.is_available()
|
|
228
|
+
except Exception:
|
|
229
|
+
available = False
|
|
230
|
+
|
|
231
|
+
if not available:
|
|
232
|
+
r.update({"status": "unavailable", "error": "not available"})
|
|
233
|
+
results.append(r)
|
|
234
|
+
continue
|
|
235
|
+
|
|
236
|
+
r.update(_bench_health_checks(transport, health_count))
|
|
237
|
+
results.append(r)
|
|
238
|
+
|
|
239
|
+
return results
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# ---------------------------------------------------------------------------
|
|
243
|
+
# Rich table renderer
|
|
244
|
+
# ---------------------------------------------------------------------------
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _render_table(results: list[dict], count: int, size: int, health_count: int) -> None:
|
|
248
|
+
"""Render benchmark results as a Rich table with a fastest-transport summary."""
|
|
249
|
+
table = Table(
|
|
250
|
+
title=f"SKComm Transport Benchmark [{count} msgs × {size}B | health×{health_count}]",
|
|
251
|
+
show_header=True,
|
|
252
|
+
header_style="bold magenta",
|
|
253
|
+
)
|
|
254
|
+
table.add_column("Transport", style="cyan", no_wrap=True)
|
|
255
|
+
table.add_column("Status", no_wrap=True)
|
|
256
|
+
table.add_column("Mode", style="dim", no_wrap=True)
|
|
257
|
+
table.add_column("p50 (ms)", justify="right")
|
|
258
|
+
table.add_column("p95 (ms)", justify="right")
|
|
259
|
+
table.add_column("p99 (ms)", justify="right")
|
|
260
|
+
table.add_column("Throughput", justify="right")
|
|
261
|
+
table.add_column("Errors", justify="right")
|
|
262
|
+
|
|
263
|
+
for r in results:
|
|
264
|
+
status = r.get("status", "unknown")
|
|
265
|
+
|
|
266
|
+
if status == "ok":
|
|
267
|
+
status_str = "[green]ok[/]"
|
|
268
|
+
mode_str = r.get("mode", "")
|
|
269
|
+
p50 = str(r.get("p50_ms") or "—")
|
|
270
|
+
p95 = str(r.get("p95_ms") or "—")
|
|
271
|
+
p99 = str(r.get("p99_ms") or "—")
|
|
272
|
+
tput = f"{r.get('throughput_msg_s', 0):.1f} msg/s"
|
|
273
|
+
err_count = r.get("errors", 0)
|
|
274
|
+
err_rate = r.get("error_rate", 0.0)
|
|
275
|
+
err_str = "[green]0[/]" if err_count == 0 else f"[red]{err_count} ({err_rate:.0%})[/]"
|
|
276
|
+
|
|
277
|
+
elif status == "unavailable":
|
|
278
|
+
status_str = "[dim]unavailable[/]"
|
|
279
|
+
err = r.get("error", "")
|
|
280
|
+
mode_str = err[:40] if err else ""
|
|
281
|
+
p50 = p95 = p99 = tput = "—"
|
|
282
|
+
err_str = ""
|
|
283
|
+
|
|
284
|
+
else:
|
|
285
|
+
status_str = "[red]error[/]"
|
|
286
|
+
mode_str = (r.get("error") or "")[:40]
|
|
287
|
+
p50 = p95 = p99 = tput = "—"
|
|
288
|
+
err_str = ""
|
|
289
|
+
|
|
290
|
+
table.add_row(r["transport"], status_str, mode_str, p50, p95, p99, tput, err_str)
|
|
291
|
+
|
|
292
|
+
console.print(table)
|
|
293
|
+
|
|
294
|
+
ok_results = [r for r in results if r.get("status") == "ok" and r.get("p50_ms") is not None]
|
|
295
|
+
if ok_results:
|
|
296
|
+
fastest = min(ok_results, key=lambda r: r.get("p50_ms") or float("inf"))
|
|
297
|
+
console.print(
|
|
298
|
+
f"\n[bold]Fastest (p50):[/] [green]{fastest['transport']}[/] "
|
|
299
|
+
f"— {fastest['p50_ms']} ms ([dim]{fastest.get('mode')}[/])"
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
# ---------------------------------------------------------------------------
|
|
304
|
+
# CLI registration
|
|
305
|
+
# ---------------------------------------------------------------------------
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def register_bench_commands(main: click.Group) -> None:
|
|
309
|
+
"""Register the ``skcapstone bench`` command."""
|
|
310
|
+
|
|
311
|
+
@main.command("bench")
|
|
312
|
+
@click.option(
|
|
313
|
+
"--count", "-n", default=100, show_default=True, type=int,
|
|
314
|
+
help="Number of messages to send via the file transport loopback.",
|
|
315
|
+
)
|
|
316
|
+
@click.option(
|
|
317
|
+
"--size", default=1024, show_default=True, type=int,
|
|
318
|
+
help="Message payload size in bytes.",
|
|
319
|
+
)
|
|
320
|
+
@click.option(
|
|
321
|
+
"--health-count", default=5, show_default=True, type=int,
|
|
322
|
+
help="Number of health_check() iterations for network transports.",
|
|
323
|
+
)
|
|
324
|
+
@click.option(
|
|
325
|
+
"--transport", "-t", "transports", multiple=True,
|
|
326
|
+
help="Benchmark only this transport. Repeat to select multiple. Default: all.",
|
|
327
|
+
)
|
|
328
|
+
@click.option("--json-out", is_flag=True, help="Output raw JSON instead of a table.")
|
|
329
|
+
def bench_cmd(
|
|
330
|
+
count: int,
|
|
331
|
+
size: int,
|
|
332
|
+
health_count: int,
|
|
333
|
+
transports: tuple,
|
|
334
|
+
json_out: bool,
|
|
335
|
+
) -> None:
|
|
336
|
+
"""Benchmark SKComm transport throughput and latency.
|
|
337
|
+
|
|
338
|
+
Sends COUNT messages of SIZE bytes through each available transport
|
|
339
|
+
and reports p50/p95/p99 latency, throughput (msg/s), and error rate.
|
|
340
|
+
|
|
341
|
+
\b
|
|
342
|
+
Transport modes:
|
|
343
|
+
file — send-loop via temp-dir loopback (COUNT msgs × SIZE B)
|
|
344
|
+
syncthing/nostr
|
|
345
|
+
websocket — health_check() × HEALTH_COUNT (no real peer needed)
|
|
346
|
+
tailscale/webrtc
|
|
347
|
+
|
|
348
|
+
\b
|
|
349
|
+
Examples:
|
|
350
|
+
skcapstone bench
|
|
351
|
+
skcapstone bench -n 500 --size 4096
|
|
352
|
+
skcapstone bench -t file -t syncthing
|
|
353
|
+
skcapstone bench --json-out | jq '.[] | select(.status=="ok")'
|
|
354
|
+
"""
|
|
355
|
+
selected = list(transports)
|
|
356
|
+
|
|
357
|
+
if not json_out:
|
|
358
|
+
scope = ", ".join(selected) if selected else "all transports"
|
|
359
|
+
console.print(
|
|
360
|
+
f"[bold]SKComm Transport Benchmark[/] "
|
|
361
|
+
f"scope={scope} count={count} size={size}B "
|
|
362
|
+
f"health-count={health_count}"
|
|
363
|
+
)
|
|
364
|
+
console.print()
|
|
365
|
+
|
|
366
|
+
results = run_bench(
|
|
367
|
+
transports=selected,
|
|
368
|
+
count=count,
|
|
369
|
+
size=size,
|
|
370
|
+
health_count=health_count,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
if json_out:
|
|
374
|
+
click.echo(json.dumps(results, indent=2))
|
|
375
|
+
return
|
|
376
|
+
|
|
377
|
+
_render_table(results, count=count, size=size, health_count=health_count)
|