@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,436 @@
|
|
|
1
|
+
"""Daemon commands: start, stop, status, install, uninstall, logs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
from ._common import AGENT_HOME, console
|
|
13
|
+
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
from rich.panel import Panel
|
|
16
|
+
|
|
17
|
+
from .. import AGENT_PORTS, DEFAULT_PORT, SKCAPSTONE_ROOT
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _resolve_agent_home(agent: str | None, home: str) -> Path:
|
|
21
|
+
"""Return the effective agent home directory.
|
|
22
|
+
|
|
23
|
+
If *agent* is given the home is always
|
|
24
|
+
``~/.skcapstone/agents/<agent>/`` regardless of *home*.
|
|
25
|
+
Otherwise *home* is used verbatim (backward-compat default).
|
|
26
|
+
"""
|
|
27
|
+
if agent:
|
|
28
|
+
return (Path(SKCAPSTONE_ROOT) / "agents" / agent).expanduser()
|
|
29
|
+
return Path(home).expanduser()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _resolve_agent_port(agent: str | None, explicit_port: int | None) -> int:
|
|
33
|
+
"""Return the port for *agent*, falling back to *explicit_port* or 7777."""
|
|
34
|
+
if explicit_port is not None:
|
|
35
|
+
return explicit_port
|
|
36
|
+
if agent:
|
|
37
|
+
return AGENT_PORTS.get(agent, max(AGENT_PORTS.values(), default=DEFAULT_PORT) + 1)
|
|
38
|
+
return DEFAULT_PORT
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
_DEV_AUTH_BANNER = """\
|
|
42
|
+
[bold red]WARNING: SKCAPSTONE_DEV_AUTH is enabled[/]
|
|
43
|
+
|
|
44
|
+
[red]Dev-auth mode bypasses cryptographic identity verification.[/]
|
|
45
|
+
[red]Any caller can claim any agent identity without a valid PGP token.[/]
|
|
46
|
+
|
|
47
|
+
[yellow]This is only safe on a fully isolated development machine.[/]
|
|
48
|
+
[yellow]NEVER run with SKCAPSTONE_DEV_AUTH=true in production.[/]"""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _warn_dev_auth(auto_confirm: bool) -> None:
|
|
52
|
+
"""Print a prominent warning when dev-auth mode is active.
|
|
53
|
+
|
|
54
|
+
If *auto_confirm* is False and neither CI nor SKCAPSTONE_YES env vars are
|
|
55
|
+
set, block until the operator presses Enter to confirm they understand the
|
|
56
|
+
risk. This gives the operator a chance to abort (Ctrl+C) before the daemon
|
|
57
|
+
starts accepting unauthenticated requests.
|
|
58
|
+
"""
|
|
59
|
+
raw = os.environ.get("SKCAPSTONE_DEV_AUTH", "").strip().lower()
|
|
60
|
+
if raw not in ("1", "true", "yes"):
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
stderr_console = Console(stderr=True, highlight=False)
|
|
64
|
+
stderr_console.print()
|
|
65
|
+
stderr_console.print(
|
|
66
|
+
Panel(
|
|
67
|
+
_DEV_AUTH_BANNER,
|
|
68
|
+
title="[bold red on white] !! DEV AUTH MODE !! [/]",
|
|
69
|
+
border_style="bold red",
|
|
70
|
+
padding=(1, 4),
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
stderr_console.print()
|
|
74
|
+
|
|
75
|
+
skip = (
|
|
76
|
+
auto_confirm
|
|
77
|
+
or os.environ.get("CI", "").strip().lower() in ("1", "true", "yes")
|
|
78
|
+
or os.environ.get("SKCAPSTONE_YES", "").strip().lower() in ("1", "true", "yes")
|
|
79
|
+
)
|
|
80
|
+
if skip:
|
|
81
|
+
stderr_console.print(
|
|
82
|
+
"[yellow]Dev-auth warning acknowledged automatically (--yes / CI mode).[/]\n"
|
|
83
|
+
)
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
input(" Press Enter to acknowledge and continue, or Ctrl+C to abort... ")
|
|
88
|
+
except (KeyboardInterrupt, EOFError):
|
|
89
|
+
stderr_console.print("\n[red]Aborted.[/]")
|
|
90
|
+
sys.exit(1)
|
|
91
|
+
|
|
92
|
+
stderr_console.print()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def register_daemon_commands(main: click.Group) -> None:
|
|
96
|
+
"""Register the daemon command group."""
|
|
97
|
+
|
|
98
|
+
@main.group()
|
|
99
|
+
def daemon():
|
|
100
|
+
"""Background daemon — the agent's heartbeat.
|
|
101
|
+
|
|
102
|
+
Start the always-on daemon for inbox polling, vault sync,
|
|
103
|
+
transport health monitoring, and the local status API.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
@daemon.command("start")
|
|
107
|
+
@click.option("--agent", default=None, help="Named agent to start (e.g. opus, jarvis).")
|
|
108
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
109
|
+
@click.option("--port", default=None, type=int, help="API port (auto-assigned per agent).")
|
|
110
|
+
@click.option("--poll", default=10, help="Inbox poll interval in seconds.")
|
|
111
|
+
@click.option("--sync-interval", "sync_int", default=300, help="Vault sync interval in seconds.")
|
|
112
|
+
@click.option("--foreground", is_flag=True, help="Run in foreground (don't daemonize).")
|
|
113
|
+
@click.option("--no-consciousness", "no_consciousness", is_flag=True,
|
|
114
|
+
help="Disable the consciousness loop.")
|
|
115
|
+
@click.option("--yes", "-y", "auto_confirm", is_flag=True,
|
|
116
|
+
help="Skip interactive confirmation prompts (for CI/automation).")
|
|
117
|
+
def daemon_start(agent: str | None, home: str, port: int | None, poll: int, sync_int: int,
|
|
118
|
+
foreground: bool, no_consciousness: bool, auto_confirm: bool):
|
|
119
|
+
"""Start the sovereign agent daemon.
|
|
120
|
+
|
|
121
|
+
Runs continuously, polling for messages, syncing vault state,
|
|
122
|
+
and exposing a local HTTP API at http://127.0.0.1:<port>.
|
|
123
|
+
|
|
124
|
+
Use --agent to run a named agent instance (opus, jarvis, …).
|
|
125
|
+
Each named agent uses its own home directory, port, and PID file
|
|
126
|
+
so multiple agents can run simultaneously.
|
|
127
|
+
|
|
128
|
+
Use --foreground for debugging or systemd integration.
|
|
129
|
+
Use --no-consciousness to disable autonomous message processing.
|
|
130
|
+
|
|
131
|
+
Examples:
|
|
132
|
+
|
|
133
|
+
skcapstone daemon start --agent opus
|
|
134
|
+
|
|
135
|
+
skcapstone daemon start --agent jarvis --foreground
|
|
136
|
+
"""
|
|
137
|
+
from ..daemon import DaemonConfig, DaemonService, is_running
|
|
138
|
+
|
|
139
|
+
home_path = _resolve_agent_home(agent, home)
|
|
140
|
+
effective_port = _resolve_agent_port(agent, port)
|
|
141
|
+
|
|
142
|
+
if agent:
|
|
143
|
+
# Propagate identity to child imports that read SKCAPSTONE_AGENT.
|
|
144
|
+
os.environ["SKCAPSTONE_AGENT"] = agent
|
|
145
|
+
|
|
146
|
+
if not home_path.exists():
|
|
147
|
+
console.print("[bold red]No agent found.[/] Run skcapstone init first.")
|
|
148
|
+
sys.exit(1)
|
|
149
|
+
|
|
150
|
+
if is_running(home_path):
|
|
151
|
+
console.print("[yellow]Daemon is already running.[/]")
|
|
152
|
+
sys.exit(0)
|
|
153
|
+
|
|
154
|
+
_warn_dev_auth(auto_confirm)
|
|
155
|
+
|
|
156
|
+
config = DaemonConfig(
|
|
157
|
+
home=home_path,
|
|
158
|
+
poll_interval=poll,
|
|
159
|
+
sync_interval=sync_int,
|
|
160
|
+
port=effective_port,
|
|
161
|
+
consciousness_enabled=not no_consciousness,
|
|
162
|
+
)
|
|
163
|
+
svc = DaemonService(config)
|
|
164
|
+
|
|
165
|
+
agent_label = f"[cyan]{agent}[/]" if agent else "[dim]default[/]"
|
|
166
|
+
console.print(f"\n [green]Starting daemon[/] ({agent_label}) on port [cyan]{effective_port}[/]")
|
|
167
|
+
console.print(f" Home: {home_path}")
|
|
168
|
+
console.print(f" Poll: {poll}s | Sync: {sync_int}s")
|
|
169
|
+
consciousness_label = "[red]disabled[/]" if no_consciousness else "[green]enabled[/]"
|
|
170
|
+
console.print(f" Consciousness: {consciousness_label}")
|
|
171
|
+
console.print(f" Log: {config.log_file}")
|
|
172
|
+
console.print(f" PID: {os.getpid()}")
|
|
173
|
+
|
|
174
|
+
if foreground:
|
|
175
|
+
console.print(" [dim]Running in foreground (Ctrl+C to stop)[/]\n")
|
|
176
|
+
svc.start()
|
|
177
|
+
svc.run_forever()
|
|
178
|
+
else:
|
|
179
|
+
console.print(" [dim]Running in foreground mode (use systemd for background)[/]\n")
|
|
180
|
+
svc.start()
|
|
181
|
+
svc.run_forever()
|
|
182
|
+
|
|
183
|
+
@daemon.command("stop")
|
|
184
|
+
@click.option("--agent", default=None, help="Named agent to stop.")
|
|
185
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
186
|
+
def daemon_stop(agent: str | None, home: str):
|
|
187
|
+
"""Stop the running daemon."""
|
|
188
|
+
from ..daemon import read_pid
|
|
189
|
+
|
|
190
|
+
home_path = _resolve_agent_home(agent, home)
|
|
191
|
+
pid = read_pid(home_path)
|
|
192
|
+
|
|
193
|
+
if pid is None:
|
|
194
|
+
console.print("[yellow]Daemon is not running.[/]")
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
import signal as sig
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
os.kill(pid, sig.SIGTERM)
|
|
201
|
+
console.print(f"\n [green]Sent SIGTERM to daemon (PID {pid})[/]\n")
|
|
202
|
+
except ProcessLookupError:
|
|
203
|
+
console.print("[yellow]Daemon process not found — cleaning up PID file.[/]")
|
|
204
|
+
(home_path / "daemon.pid").unlink(missing_ok=True)
|
|
205
|
+
|
|
206
|
+
@daemon.command("status")
|
|
207
|
+
@click.option("--agent", default=None, help="Named agent to query.")
|
|
208
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
209
|
+
@click.option("--port", default=None, type=int, help="API port to query.")
|
|
210
|
+
@click.option("--json-out", is_flag=True, help="Output as JSON.")
|
|
211
|
+
def daemon_status(agent: str | None, home: str, port: int | None, json_out: bool):
|
|
212
|
+
"""Show daemon status."""
|
|
213
|
+
from ..daemon import get_daemon_status, is_running, read_pid
|
|
214
|
+
|
|
215
|
+
home_path = _resolve_agent_home(agent, home)
|
|
216
|
+
effective_port = _resolve_agent_port(agent, port)
|
|
217
|
+
pid = read_pid(home_path)
|
|
218
|
+
|
|
219
|
+
if not is_running(home_path):
|
|
220
|
+
if json_out:
|
|
221
|
+
click.echo(json.dumps({"running": False}))
|
|
222
|
+
else:
|
|
223
|
+
console.print("\n [yellow]Daemon is not running.[/]\n")
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
status = get_daemon_status(home_path, effective_port)
|
|
227
|
+
if json_out:
|
|
228
|
+
click.echo(json.dumps(status or {"running": True, "pid": pid, "api": "unreachable"}, indent=2))
|
|
229
|
+
return
|
|
230
|
+
|
|
231
|
+
if status:
|
|
232
|
+
uptime = status.get("uptime_seconds", 0)
|
|
233
|
+
h, remainder = divmod(int(uptime), 3600)
|
|
234
|
+
m, s = divmod(remainder, 60)
|
|
235
|
+
uptime_str = f"{h}h {m}m {s}s" if h else f"{m}m {s}s"
|
|
236
|
+
|
|
237
|
+
console.print()
|
|
238
|
+
console.print(
|
|
239
|
+
Panel(
|
|
240
|
+
f"PID: [bold]{status.get('pid')}[/]\n"
|
|
241
|
+
f"Uptime: [bold]{uptime_str}[/]\n"
|
|
242
|
+
f"Messages received: [bold]{status.get('messages_received', 0)}[/]\n"
|
|
243
|
+
f"Syncs completed: [bold]{status.get('syncs_completed', 0)}[/]\n"
|
|
244
|
+
f"Last poll: {status.get('last_poll') or '[dim]never[/]'}\n"
|
|
245
|
+
f"Last sync: {status.get('last_sync') or '[dim]never[/]'}\n"
|
|
246
|
+
f"API: [green]http://127.0.0.1:{effective_port}[/]",
|
|
247
|
+
title="[green]Daemon Running[/]",
|
|
248
|
+
border_style="green",
|
|
249
|
+
)
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
health = status.get("transport_health", {})
|
|
253
|
+
if health:
|
|
254
|
+
console.print("[bold]Transports:[/]")
|
|
255
|
+
for name, info in health.items():
|
|
256
|
+
if isinstance(info, dict):
|
|
257
|
+
st = info.get("status", "unknown")
|
|
258
|
+
color = {"available": "green", "degraded": "yellow"}.get(st, "red")
|
|
259
|
+
console.print(f" [{color}]{name}: {st.upper()}[/]")
|
|
260
|
+
|
|
261
|
+
errors = status.get("recent_errors", [])
|
|
262
|
+
if errors:
|
|
263
|
+
console.print(f"\n[yellow]Recent errors ({len(errors)}):[/]")
|
|
264
|
+
for err in errors[-5:]:
|
|
265
|
+
console.print(f" [dim]{err}[/]")
|
|
266
|
+
|
|
267
|
+
console.print()
|
|
268
|
+
else:
|
|
269
|
+
console.print(f"\n [green]Daemon running[/] (PID {pid})")
|
|
270
|
+
console.print(f" [yellow]API unreachable on port {effective_port}[/]\n")
|
|
271
|
+
|
|
272
|
+
@daemon.command("install")
|
|
273
|
+
def daemon_install():
|
|
274
|
+
"""Install the daemon as a systemd user service.
|
|
275
|
+
|
|
276
|
+
Copies unit files to ~/.config/systemd/user/, enables at login,
|
|
277
|
+
and starts immediately. No root required.
|
|
278
|
+
|
|
279
|
+
Examples:
|
|
280
|
+
|
|
281
|
+
skcapstone daemon install
|
|
282
|
+
"""
|
|
283
|
+
from ..systemd import install_service, systemd_available
|
|
284
|
+
|
|
285
|
+
if not systemd_available():
|
|
286
|
+
console.print("[red]systemd user session not available.[/]")
|
|
287
|
+
console.print("[dim]This command requires a Linux system with systemd.[/]")
|
|
288
|
+
raise SystemExit(1)
|
|
289
|
+
|
|
290
|
+
console.print("\n[cyan]Installing skcapstone systemd service...[/]")
|
|
291
|
+
result = install_service()
|
|
292
|
+
|
|
293
|
+
if result["installed"]:
|
|
294
|
+
console.print("[green] Unit files installed.[/]")
|
|
295
|
+
if result["enabled"]:
|
|
296
|
+
console.print("[green] Service enabled at login.[/]")
|
|
297
|
+
if result["started"]:
|
|
298
|
+
console.print("[green] Service started.[/]")
|
|
299
|
+
console.print()
|
|
300
|
+
|
|
301
|
+
if not result["installed"]:
|
|
302
|
+
console.print("[red]Installation failed. Check logs.[/]")
|
|
303
|
+
raise SystemExit(1)
|
|
304
|
+
|
|
305
|
+
@daemon.command("uninstall")
|
|
306
|
+
def daemon_uninstall():
|
|
307
|
+
"""Uninstall the systemd user service.
|
|
308
|
+
|
|
309
|
+
Stops, disables, and removes the unit files.
|
|
310
|
+
|
|
311
|
+
Examples:
|
|
312
|
+
|
|
313
|
+
skcapstone daemon uninstall
|
|
314
|
+
"""
|
|
315
|
+
from ..systemd import uninstall_service
|
|
316
|
+
|
|
317
|
+
console.print("\n[cyan]Uninstalling skcapstone systemd service...[/]")
|
|
318
|
+
result = uninstall_service()
|
|
319
|
+
|
|
320
|
+
if result["stopped"]:
|
|
321
|
+
console.print("[green] Service stopped.[/]")
|
|
322
|
+
if result["disabled"]:
|
|
323
|
+
console.print("[green] Service disabled.[/]")
|
|
324
|
+
if result["removed"]:
|
|
325
|
+
console.print("[green] Unit files removed.[/]")
|
|
326
|
+
console.print()
|
|
327
|
+
|
|
328
|
+
@daemon.command("components")
|
|
329
|
+
@click.option("--agent", default=None, help="Named agent to query.")
|
|
330
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
331
|
+
@click.option("--port", default=None, type=int, help="API port to query.")
|
|
332
|
+
@click.option("--json-out", is_flag=True, help="Output as JSON.")
|
|
333
|
+
def daemon_components(agent: str | None, home: str, port: int | None, json_out: bool):
|
|
334
|
+
"""Show per-component health (alive/dead/restarting).
|
|
335
|
+
|
|
336
|
+
Queries the running daemon for the status of each subsystem:
|
|
337
|
+
poll, health, sync, housekeeping, healing, consciousness,
|
|
338
|
+
scheduler, and heartbeat.
|
|
339
|
+
|
|
340
|
+
Examples:
|
|
341
|
+
|
|
342
|
+
skcapstone daemon components
|
|
343
|
+
|
|
344
|
+
skcapstone daemon components --json-out
|
|
345
|
+
"""
|
|
346
|
+
import urllib.error
|
|
347
|
+
import urllib.request
|
|
348
|
+
|
|
349
|
+
home_path = _resolve_agent_home(agent, home)
|
|
350
|
+
effective_port = _resolve_agent_port(agent, port)
|
|
351
|
+
|
|
352
|
+
try:
|
|
353
|
+
url = f"http://127.0.0.1:{effective_port}/api/v1/components"
|
|
354
|
+
with urllib.request.urlopen(url, timeout=5) as resp:
|
|
355
|
+
data = json.loads(resp.read())
|
|
356
|
+
except (urllib.error.URLError, OSError, json.JSONDecodeError) as exc:
|
|
357
|
+
if json_out:
|
|
358
|
+
click.echo(json.dumps({"error": str(exc)}))
|
|
359
|
+
else:
|
|
360
|
+
console.print(f"\n [red]Cannot reach daemon on port {effective_port}.[/]")
|
|
361
|
+
console.print(" [dim]Is the daemon running? Try: skcapstone daemon start[/]\n")
|
|
362
|
+
return
|
|
363
|
+
|
|
364
|
+
if json_out:
|
|
365
|
+
click.echo(json.dumps(data, indent=2))
|
|
366
|
+
return
|
|
367
|
+
|
|
368
|
+
components = data.get("components", {})
|
|
369
|
+
if not components:
|
|
370
|
+
console.print("\n [yellow]No component data returned.[/]\n")
|
|
371
|
+
return
|
|
372
|
+
|
|
373
|
+
from rich.table import Table
|
|
374
|
+
|
|
375
|
+
table = Table(title="Daemon Components", border_style="dim")
|
|
376
|
+
table.add_column("Component", style="cyan", no_wrap=True)
|
|
377
|
+
table.add_column("Status", no_wrap=True)
|
|
378
|
+
table.add_column("Restarts", justify="right")
|
|
379
|
+
table.add_column("Heartbeat age", justify="right")
|
|
380
|
+
table.add_column("Auto-restart", justify="center")
|
|
381
|
+
table.add_column("Last error", style="dim", max_width=40)
|
|
382
|
+
|
|
383
|
+
status_colors = {
|
|
384
|
+
"alive": "green",
|
|
385
|
+
"dead": "red",
|
|
386
|
+
"restarting": "yellow",
|
|
387
|
+
"disabled": "dim",
|
|
388
|
+
"pending": "blue",
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
for name, info in sorted(components.items()):
|
|
392
|
+
status = info.get("status", "unknown")
|
|
393
|
+
color = status_colors.get(status, "white")
|
|
394
|
+
restarts = str(info.get("restart_count", 0))
|
|
395
|
+
age = info.get("heartbeat_age_seconds")
|
|
396
|
+
age_str = f"{age}s" if age is not None else "—"
|
|
397
|
+
auto = "[green]yes[/]" if info.get("auto_restart") else "[dim]no[/]"
|
|
398
|
+
last_error = (info.get("last_error") or "")[:40] or "—"
|
|
399
|
+
table.add_row(
|
|
400
|
+
name,
|
|
401
|
+
f"[{color}]{status}[/]",
|
|
402
|
+
restarts,
|
|
403
|
+
age_str,
|
|
404
|
+
auto,
|
|
405
|
+
last_error,
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
console.print()
|
|
409
|
+
console.print(table)
|
|
410
|
+
console.print()
|
|
411
|
+
|
|
412
|
+
@daemon.command("logs")
|
|
413
|
+
@click.option("--lines", "-n", default=50, help="Number of lines (default: 50).")
|
|
414
|
+
@click.option("--follow", "-f", is_flag=True, help="Show the command to follow logs live.")
|
|
415
|
+
def daemon_logs(lines: int, follow: bool):
|
|
416
|
+
"""Show daemon logs from journald.
|
|
417
|
+
|
|
418
|
+
Examples:
|
|
419
|
+
|
|
420
|
+
skcapstone daemon logs
|
|
421
|
+
|
|
422
|
+
skcapstone daemon logs -n 100
|
|
423
|
+
|
|
424
|
+
skcapstone daemon logs -f
|
|
425
|
+
"""
|
|
426
|
+
from ..systemd import service_logs
|
|
427
|
+
|
|
428
|
+
if follow:
|
|
429
|
+
cmd = service_logs(follow=True)
|
|
430
|
+
console.print(f"\n Run: [bold cyan]{cmd}[/]\n")
|
|
431
|
+
else:
|
|
432
|
+
output = service_logs(lines=lines)
|
|
433
|
+
if output.strip():
|
|
434
|
+
click.echo(output)
|
|
435
|
+
else:
|
|
436
|
+
console.print("[dim]No logs found. Is the service installed?[/]")
|