@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,307 @@
|
|
|
1
|
+
"""Tests for CapAuth identity integration.
|
|
2
|
+
|
|
3
|
+
Covers both paths: real CapAuth profile present vs placeholder fallback.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from types import SimpleNamespace
|
|
12
|
+
from unittest.mock import MagicMock, patch
|
|
13
|
+
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
from skcapstone.discovery import (
|
|
17
|
+
_sync_identity_json,
|
|
18
|
+
_try_load_capauth_profile,
|
|
19
|
+
discover_identity,
|
|
20
|
+
)
|
|
21
|
+
from skcapstone.models import IdentityState, PillarStatus
|
|
22
|
+
from skcapstone.pillars.identity import (
|
|
23
|
+
_generate_placeholder_fingerprint,
|
|
24
|
+
_try_init_capauth,
|
|
25
|
+
generate_identity,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _fake_profile(
|
|
30
|
+
fingerprint: str = "A" * 40,
|
|
31
|
+
name: str = "Opus",
|
|
32
|
+
email: str = "opus@capauth.local",
|
|
33
|
+
) -> SimpleNamespace:
|
|
34
|
+
"""Build a fake SovereignProfile-like object for testing."""
|
|
35
|
+
return SimpleNamespace(
|
|
36
|
+
key_info=SimpleNamespace(
|
|
37
|
+
fingerprint=fingerprint,
|
|
38
|
+
public_key_path="/tmp/public.asc",
|
|
39
|
+
created=datetime(2026, 1, 1, tzinfo=timezone.utc),
|
|
40
|
+
),
|
|
41
|
+
entity=SimpleNamespace(name=name, email=email),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
# discover_identity: real CapAuth path
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TestDiscoverWithCapAuth:
|
|
51
|
+
"""Tests for discover_identity when a real CapAuth profile exists."""
|
|
52
|
+
|
|
53
|
+
def test_uses_capauth_fingerprint(self, tmp_agent_home: Path):
|
|
54
|
+
"""discover_identity returns the real CapAuth fingerprint."""
|
|
55
|
+
fake = _fake_profile(fingerprint="B" * 40, name="Jarvis")
|
|
56
|
+
|
|
57
|
+
with patch(
|
|
58
|
+
"skcapstone.discovery._try_load_capauth_profile",
|
|
59
|
+
return_value=IdentityState(
|
|
60
|
+
fingerprint=fake.key_info.fingerprint,
|
|
61
|
+
name=fake.entity.name,
|
|
62
|
+
email=fake.entity.email,
|
|
63
|
+
key_path=Path(fake.key_info.public_key_path),
|
|
64
|
+
created_at=fake.key_info.created,
|
|
65
|
+
status=PillarStatus.ACTIVE,
|
|
66
|
+
),
|
|
67
|
+
):
|
|
68
|
+
state = discover_identity(tmp_agent_home)
|
|
69
|
+
|
|
70
|
+
assert state.fingerprint == "B" * 40
|
|
71
|
+
assert state.name == "Jarvis"
|
|
72
|
+
assert state.status == PillarStatus.ACTIVE
|
|
73
|
+
|
|
74
|
+
def test_syncs_identity_json(self, tmp_agent_home: Path):
|
|
75
|
+
"""discover_identity writes identity.json with capauth_managed=True."""
|
|
76
|
+
identity_dir = tmp_agent_home / "identity"
|
|
77
|
+
identity_dir.mkdir(parents=True, exist_ok=True)
|
|
78
|
+
|
|
79
|
+
fake = _fake_profile(fingerprint="C" * 40)
|
|
80
|
+
with patch(
|
|
81
|
+
"skcapstone.discovery._try_load_capauth_profile",
|
|
82
|
+
return_value=IdentityState(
|
|
83
|
+
fingerprint="C" * 40,
|
|
84
|
+
name="Opus",
|
|
85
|
+
email="opus@capauth.local",
|
|
86
|
+
key_path=Path("/tmp/public.asc"),
|
|
87
|
+
status=PillarStatus.ACTIVE,
|
|
88
|
+
),
|
|
89
|
+
):
|
|
90
|
+
discover_identity(tmp_agent_home)
|
|
91
|
+
|
|
92
|
+
manifest = json.loads((identity_dir / "identity.json").read_text())
|
|
93
|
+
assert manifest["fingerprint"] == "C" * 40
|
|
94
|
+
assert manifest["capauth_managed"] is True
|
|
95
|
+
|
|
96
|
+
def test_replaces_placeholder_fingerprint(self, tmp_agent_home: Path):
|
|
97
|
+
"""When upgrading from placeholder to real keys, identity.json updates."""
|
|
98
|
+
identity_dir = tmp_agent_home / "identity"
|
|
99
|
+
identity_dir.mkdir(parents=True, exist_ok=True)
|
|
100
|
+
|
|
101
|
+
old_manifest = {
|
|
102
|
+
"name": "test",
|
|
103
|
+
"email": "test@skcapstone.local",
|
|
104
|
+
"fingerprint": _generate_placeholder_fingerprint("test"),
|
|
105
|
+
"capauth_managed": False,
|
|
106
|
+
}
|
|
107
|
+
(identity_dir / "identity.json").write_text(json.dumps(old_manifest))
|
|
108
|
+
|
|
109
|
+
with patch(
|
|
110
|
+
"skcapstone.discovery._try_load_capauth_profile",
|
|
111
|
+
return_value=IdentityState(
|
|
112
|
+
fingerprint="D" * 40,
|
|
113
|
+
name="test",
|
|
114
|
+
email="test@capauth.local",
|
|
115
|
+
status=PillarStatus.ACTIVE,
|
|
116
|
+
),
|
|
117
|
+
):
|
|
118
|
+
state = discover_identity(tmp_agent_home)
|
|
119
|
+
|
|
120
|
+
assert state.fingerprint == "D" * 40
|
|
121
|
+
updated = json.loads((identity_dir / "identity.json").read_text())
|
|
122
|
+
assert updated["fingerprint"] == "D" * 40
|
|
123
|
+
assert updated["capauth_managed"] is True
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# ---------------------------------------------------------------------------
|
|
127
|
+
# discover_identity: placeholder fallback
|
|
128
|
+
# ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class TestDiscoverWithoutCapAuth:
|
|
132
|
+
"""Tests for discover_identity when CapAuth is not available."""
|
|
133
|
+
|
|
134
|
+
def test_reads_existing_identity_json(self, tmp_agent_home: Path):
|
|
135
|
+
"""Falls back to reading identity.json when no CapAuth profile."""
|
|
136
|
+
identity_dir = tmp_agent_home / "identity"
|
|
137
|
+
identity_dir.mkdir(parents=True, exist_ok=True)
|
|
138
|
+
|
|
139
|
+
manifest = {
|
|
140
|
+
"name": "fallback-agent",
|
|
141
|
+
"email": "fallback@skcapstone.local",
|
|
142
|
+
"fingerprint": "F" * 40,
|
|
143
|
+
"capauth_managed": False,
|
|
144
|
+
"created_at": "2026-01-01T00:00:00+00:00",
|
|
145
|
+
}
|
|
146
|
+
(identity_dir / "identity.json").write_text(json.dumps(manifest))
|
|
147
|
+
|
|
148
|
+
with patch("skcapstone.discovery._try_load_capauth_profile", return_value=None):
|
|
149
|
+
state = discover_identity(tmp_agent_home)
|
|
150
|
+
|
|
151
|
+
assert state.fingerprint == "F" * 40
|
|
152
|
+
assert state.name == "fallback-agent"
|
|
153
|
+
assert state.status == PillarStatus.DEGRADED
|
|
154
|
+
|
|
155
|
+
def test_no_identity_at_all(self, tmp_agent_home: Path):
|
|
156
|
+
"""No identity.json and no CapAuth returns MISSING."""
|
|
157
|
+
with patch("skcapstone.discovery._try_load_capauth_profile", return_value=None):
|
|
158
|
+
state = discover_identity(tmp_agent_home)
|
|
159
|
+
|
|
160
|
+
assert state.status == PillarStatus.MISSING
|
|
161
|
+
|
|
162
|
+
def test_capauth_managed_true_is_active(self, tmp_agent_home: Path):
|
|
163
|
+
"""identity.json with capauth_managed=True is ACTIVE even without profile load."""
|
|
164
|
+
identity_dir = tmp_agent_home / "identity"
|
|
165
|
+
identity_dir.mkdir(parents=True, exist_ok=True)
|
|
166
|
+
|
|
167
|
+
manifest = {
|
|
168
|
+
"name": "real-agent",
|
|
169
|
+
"email": "real@capauth.local",
|
|
170
|
+
"fingerprint": "E" * 40,
|
|
171
|
+
"capauth_managed": True,
|
|
172
|
+
}
|
|
173
|
+
(identity_dir / "identity.json").write_text(json.dumps(manifest))
|
|
174
|
+
|
|
175
|
+
with patch("skcapstone.discovery._try_load_capauth_profile", return_value=None):
|
|
176
|
+
state = discover_identity(tmp_agent_home)
|
|
177
|
+
|
|
178
|
+
assert state.status == PillarStatus.ACTIVE
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# ---------------------------------------------------------------------------
|
|
182
|
+
# generate_identity: init path
|
|
183
|
+
# ---------------------------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class TestGenerateIdentity:
|
|
187
|
+
"""Tests for generate_identity (the skcapstone init path)."""
|
|
188
|
+
|
|
189
|
+
def test_placeholder_when_no_capauth(self, tmp_agent_home: Path):
|
|
190
|
+
"""Without CapAuth, generates a placeholder fingerprint."""
|
|
191
|
+
with patch(
|
|
192
|
+
"skcapstone.pillars.identity._try_init_capauth", return_value=None
|
|
193
|
+
):
|
|
194
|
+
state = generate_identity(tmp_agent_home, "test-agent")
|
|
195
|
+
|
|
196
|
+
assert state.status == PillarStatus.DEGRADED
|
|
197
|
+
assert state.fingerprint == _generate_placeholder_fingerprint("test-agent")
|
|
198
|
+
assert len(state.fingerprint) == 40
|
|
199
|
+
|
|
200
|
+
def test_active_when_capauth_works(self, tmp_agent_home: Path):
|
|
201
|
+
"""With CapAuth, uses real keys and sets ACTIVE."""
|
|
202
|
+
fake_state = IdentityState(
|
|
203
|
+
fingerprint="A" * 40,
|
|
204
|
+
key_path=Path("/tmp/pub.asc"),
|
|
205
|
+
name="test",
|
|
206
|
+
email="test@capauth.local",
|
|
207
|
+
status=PillarStatus.ACTIVE,
|
|
208
|
+
)
|
|
209
|
+
with patch(
|
|
210
|
+
"skcapstone.pillars.identity._try_init_capauth", return_value=fake_state
|
|
211
|
+
):
|
|
212
|
+
state = generate_identity(tmp_agent_home, "test")
|
|
213
|
+
|
|
214
|
+
assert state.fingerprint == "A" * 40
|
|
215
|
+
assert state.status == PillarStatus.ACTIVE
|
|
216
|
+
|
|
217
|
+
def test_identity_json_written(self, tmp_agent_home: Path):
|
|
218
|
+
"""identity.json is always written, even with placeholders."""
|
|
219
|
+
with patch(
|
|
220
|
+
"skcapstone.pillars.identity._try_init_capauth", return_value=None
|
|
221
|
+
):
|
|
222
|
+
generate_identity(tmp_agent_home, "writer-test")
|
|
223
|
+
|
|
224
|
+
assert (tmp_agent_home / "identity" / "identity.json").exists()
|
|
225
|
+
data = json.loads(
|
|
226
|
+
(tmp_agent_home / "identity" / "identity.json").read_text()
|
|
227
|
+
)
|
|
228
|
+
assert data["name"] == "writer-test"
|
|
229
|
+
assert data["capauth_managed"] is False
|
|
230
|
+
|
|
231
|
+
def test_placeholder_deterministic(self):
|
|
232
|
+
"""Placeholder fingerprints are deterministic for the same name."""
|
|
233
|
+
fp1 = _generate_placeholder_fingerprint("agent-x")
|
|
234
|
+
fp2 = _generate_placeholder_fingerprint("agent-x")
|
|
235
|
+
fp3 = _generate_placeholder_fingerprint("agent-y")
|
|
236
|
+
assert fp1 == fp2
|
|
237
|
+
assert fp1 != fp3
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
# ---------------------------------------------------------------------------
|
|
241
|
+
# _sync_identity_json
|
|
242
|
+
# ---------------------------------------------------------------------------
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class TestSyncIdentityJson:
|
|
246
|
+
"""Tests for the identity.json sync helper."""
|
|
247
|
+
|
|
248
|
+
def test_creates_file_if_missing(self, tmp_path: Path):
|
|
249
|
+
"""Creates identity.json from scratch when it doesn't exist."""
|
|
250
|
+
identity_dir = tmp_path / "identity"
|
|
251
|
+
state = IdentityState(
|
|
252
|
+
fingerprint="X" * 40,
|
|
253
|
+
name="new-agent",
|
|
254
|
+
email="new@capauth.local",
|
|
255
|
+
status=PillarStatus.ACTIVE,
|
|
256
|
+
)
|
|
257
|
+
_sync_identity_json(identity_dir, state)
|
|
258
|
+
|
|
259
|
+
assert (identity_dir / "identity.json").exists()
|
|
260
|
+
data = json.loads((identity_dir / "identity.json").read_text())
|
|
261
|
+
assert data["fingerprint"] == "X" * 40
|
|
262
|
+
assert data["capauth_managed"] is True
|
|
263
|
+
|
|
264
|
+
def test_skips_write_when_unchanged(self, tmp_path: Path):
|
|
265
|
+
"""Does not rewrite identity.json when fingerprint matches."""
|
|
266
|
+
identity_dir = tmp_path / "identity"
|
|
267
|
+
identity_dir.mkdir(parents=True)
|
|
268
|
+
|
|
269
|
+
existing = {
|
|
270
|
+
"name": "same",
|
|
271
|
+
"email": "same@capauth.local",
|
|
272
|
+
"fingerprint": "Y" * 40,
|
|
273
|
+
"capauth_managed": True,
|
|
274
|
+
"created_at": None,
|
|
275
|
+
}
|
|
276
|
+
path = identity_dir / "identity.json"
|
|
277
|
+
path.write_text(json.dumps(existing))
|
|
278
|
+
mtime_before = path.stat().st_mtime
|
|
279
|
+
|
|
280
|
+
state = IdentityState(
|
|
281
|
+
fingerprint="Y" * 40,
|
|
282
|
+
name="same",
|
|
283
|
+
email="same@capauth.local",
|
|
284
|
+
status=PillarStatus.ACTIVE,
|
|
285
|
+
)
|
|
286
|
+
_sync_identity_json(identity_dir, state)
|
|
287
|
+
|
|
288
|
+
assert path.stat().st_mtime == mtime_before
|
|
289
|
+
|
|
290
|
+
def test_updates_when_fingerprint_changes(self, tmp_path: Path):
|
|
291
|
+
"""Rewrites identity.json when the fingerprint is different."""
|
|
292
|
+
identity_dir = tmp_path / "identity"
|
|
293
|
+
identity_dir.mkdir(parents=True)
|
|
294
|
+
|
|
295
|
+
old = {"fingerprint": "OLD" + "0" * 37, "capauth_managed": False}
|
|
296
|
+
(identity_dir / "identity.json").write_text(json.dumps(old))
|
|
297
|
+
|
|
298
|
+
state = IdentityState(
|
|
299
|
+
fingerprint="NEW" + "1" * 37,
|
|
300
|
+
name="upgraded",
|
|
301
|
+
status=PillarStatus.ACTIVE,
|
|
302
|
+
)
|
|
303
|
+
_sync_identity_json(identity_dir, state)
|
|
304
|
+
|
|
305
|
+
data = json.loads((identity_dir / "identity.json").read_text())
|
|
306
|
+
assert data["fingerprint"] == "NEW" + "1" * 37
|
|
307
|
+
assert data["capauth_managed"] is True
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Unit tests for the identity pillar module."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from unittest.mock import MagicMock, patch
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from skcapstone.models import PillarStatus
|
|
12
|
+
from skcapstone.pillars.identity import (
|
|
13
|
+
_generate_placeholder_fingerprint,
|
|
14
|
+
generate_identity,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestGenerateIdentity:
|
|
19
|
+
"""Tests for generate_identity()."""
|
|
20
|
+
|
|
21
|
+
def test_creates_identity_directory(self, tmp_agent_home: Path):
|
|
22
|
+
generate_identity(tmp_agent_home, "test-agent")
|
|
23
|
+
assert (tmp_agent_home / "identity").is_dir()
|
|
24
|
+
|
|
25
|
+
def test_writes_identity_json(self, tmp_agent_home: Path):
|
|
26
|
+
generate_identity(tmp_agent_home, "test-agent")
|
|
27
|
+
assert (tmp_agent_home / "identity" / "identity.json").exists()
|
|
28
|
+
|
|
29
|
+
def test_state_name_matches(self, tmp_agent_home: Path):
|
|
30
|
+
state = generate_identity(tmp_agent_home, "my-agent")
|
|
31
|
+
assert state.name == "my-agent"
|
|
32
|
+
|
|
33
|
+
def test_explicit_email_used(self, tmp_agent_home: Path):
|
|
34
|
+
state = generate_identity(tmp_agent_home, "opus", email="opus@skworld.io")
|
|
35
|
+
assert state.email == "opus@skworld.io"
|
|
36
|
+
|
|
37
|
+
def test_email_auto_generated_from_name(self, tmp_agent_home: Path):
|
|
38
|
+
state = generate_identity(tmp_agent_home, "My Agent")
|
|
39
|
+
assert state.email == "my-agent@skcapstone.local"
|
|
40
|
+
|
|
41
|
+
def test_fingerprint_always_set(self, tmp_agent_home: Path):
|
|
42
|
+
state = generate_identity(tmp_agent_home, "test")
|
|
43
|
+
assert state.fingerprint is not None
|
|
44
|
+
assert len(state.fingerprint) == 40
|
|
45
|
+
|
|
46
|
+
def test_identity_json_has_required_fields(self, tmp_agent_home: Path):
|
|
47
|
+
generate_identity(tmp_agent_home, "agent-x")
|
|
48
|
+
data = json.loads(
|
|
49
|
+
(tmp_agent_home / "identity" / "identity.json").read_text()
|
|
50
|
+
)
|
|
51
|
+
for field in ("name", "email", "fingerprint", "created_at"):
|
|
52
|
+
assert field in data, f"missing field: {field}"
|
|
53
|
+
|
|
54
|
+
def test_identity_json_name_matches(self, tmp_agent_home: Path):
|
|
55
|
+
generate_identity(tmp_agent_home, "lumina")
|
|
56
|
+
data = json.loads(
|
|
57
|
+
(tmp_agent_home / "identity" / "identity.json").read_text()
|
|
58
|
+
)
|
|
59
|
+
assert data["name"] == "lumina"
|
|
60
|
+
|
|
61
|
+
def test_idempotent_second_call_succeeds(self, tmp_agent_home: Path):
|
|
62
|
+
generate_identity(tmp_agent_home, "agent-a")
|
|
63
|
+
state2 = generate_identity(tmp_agent_home, "agent-a")
|
|
64
|
+
assert state2.name == "agent-a"
|
|
65
|
+
|
|
66
|
+
def test_degraded_status_without_capauth(self, tmp_agent_home: Path):
|
|
67
|
+
"""Without capauth installed the status should be DEGRADED."""
|
|
68
|
+
with patch.dict("sys.modules", {"capauth": None, "capauth.profile": None, "capauth.keys": None}):
|
|
69
|
+
state = generate_identity(tmp_agent_home, "test-agent")
|
|
70
|
+
assert state.status == PillarStatus.DEGRADED
|
|
71
|
+
|
|
72
|
+
def test_capauth_managed_false_without_capauth(self, tmp_agent_home: Path):
|
|
73
|
+
with patch.dict("sys.modules", {"capauth": None, "capauth.profile": None, "capauth.keys": None}):
|
|
74
|
+
generate_identity(tmp_agent_home, "no-capauth")
|
|
75
|
+
data = json.loads(
|
|
76
|
+
(tmp_agent_home / "identity" / "identity.json").read_text()
|
|
77
|
+
)
|
|
78
|
+
assert data["capauth_managed"] is False
|
|
79
|
+
|
|
80
|
+
def test_active_status_with_capauth_profile(self, tmp_agent_home: Path):
|
|
81
|
+
"""When capauth.profile.load_profile succeeds, status is ACTIVE."""
|
|
82
|
+
mock_profile = MagicMock()
|
|
83
|
+
mock_profile.key_info.fingerprint = "A" * 40
|
|
84
|
+
mock_profile.key_info.public_key_path = str(tmp_agent_home / "agent.pub")
|
|
85
|
+
mock_profile.entity.name = "opus"
|
|
86
|
+
mock_profile.entity.email = "opus@test"
|
|
87
|
+
|
|
88
|
+
mock_capauth_profile = MagicMock()
|
|
89
|
+
mock_capauth_profile.load_profile.return_value = mock_profile
|
|
90
|
+
|
|
91
|
+
with patch.dict("sys.modules", {"capauth": MagicMock(), "capauth.profile": mock_capauth_profile}):
|
|
92
|
+
state = generate_identity(tmp_agent_home, "opus")
|
|
93
|
+
|
|
94
|
+
assert state.status == PillarStatus.ACTIVE
|
|
95
|
+
assert state.fingerprint == "A" * 40
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class TestPlaceholderFingerprint:
|
|
99
|
+
"""Tests for _generate_placeholder_fingerprint()."""
|
|
100
|
+
|
|
101
|
+
def test_length_is_40(self):
|
|
102
|
+
fp = _generate_placeholder_fingerprint("test-agent")
|
|
103
|
+
assert len(fp) == 40
|
|
104
|
+
|
|
105
|
+
def test_is_uppercase_hex(self):
|
|
106
|
+
fp = _generate_placeholder_fingerprint("agent")
|
|
107
|
+
assert all(c in "0123456789ABCDEF" for c in fp)
|
|
108
|
+
|
|
109
|
+
def test_deterministic(self):
|
|
110
|
+
fp1 = _generate_placeholder_fingerprint("opus")
|
|
111
|
+
fp2 = _generate_placeholder_fingerprint("opus")
|
|
112
|
+
assert fp1 == fp2
|
|
113
|
+
|
|
114
|
+
def test_different_names_different_fingerprints(self):
|
|
115
|
+
fp1 = _generate_placeholder_fingerprint("opus")
|
|
116
|
+
fp2 = _generate_placeholder_fingerprint("lumina")
|
|
117
|
+
assert fp1 != fp2
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Tests for the install wizard — path selection, confirmation, routing."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from unittest.mock import MagicMock, patch
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from skcapstone.install_wizard import (
|
|
11
|
+
PATH_LABELS,
|
|
12
|
+
_wait_for_sync,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestPathLabels:
|
|
17
|
+
"""Tests for path label definitions."""
|
|
18
|
+
|
|
19
|
+
def test_all_three_paths_exist(self) -> None:
|
|
20
|
+
"""All three install paths have labels."""
|
|
21
|
+
assert 1 in PATH_LABELS
|
|
22
|
+
assert 2 in PATH_LABELS
|
|
23
|
+
assert 3 in PATH_LABELS
|
|
24
|
+
|
|
25
|
+
def test_labels_are_human_readable(self) -> None:
|
|
26
|
+
"""Labels contain no jargon."""
|
|
27
|
+
for label in PATH_LABELS.values():
|
|
28
|
+
assert len(label) > 10
|
|
29
|
+
assert "sovereign singularity" not in label.lower()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class TestWaitForSync:
|
|
33
|
+
"""Tests for _wait_for_sync helper."""
|
|
34
|
+
|
|
35
|
+
def test_returns_true_when_identity_exists(self, tmp_path: Path) -> None:
|
|
36
|
+
"""Returns True immediately if identity.json exists."""
|
|
37
|
+
(tmp_path / "identity.json").write_text("{}")
|
|
38
|
+
assert _wait_for_sync(tmp_path, timeout_seconds=1) is True
|
|
39
|
+
|
|
40
|
+
def test_returns_true_when_registry_exists(self, tmp_path: Path) -> None:
|
|
41
|
+
"""Returns True if vault-registry.json exists."""
|
|
42
|
+
(tmp_path / "vault-registry.json").write_text("{}")
|
|
43
|
+
assert _wait_for_sync(tmp_path, timeout_seconds=1) is True
|
|
44
|
+
|
|
45
|
+
def test_returns_true_when_auth_key_exists(self, tmp_path: Path) -> None:
|
|
46
|
+
"""Returns True if tailscale.key.gpg exists."""
|
|
47
|
+
(tmp_path / "tailscale.key.gpg").write_bytes(b"encrypted")
|
|
48
|
+
assert _wait_for_sync(tmp_path, timeout_seconds=1) is True
|
|
49
|
+
|
|
50
|
+
def test_returns_false_when_empty(self, tmp_path: Path) -> None:
|
|
51
|
+
"""Returns False after timeout when no files exist."""
|
|
52
|
+
assert _wait_for_sync(tmp_path, timeout_seconds=1) is False
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class TestPathUpdateExisting:
|
|
56
|
+
"""Tests for path 3 — update existing node."""
|
|
57
|
+
|
|
58
|
+
def test_exits_if_no_home(self, tmp_path: Path) -> None:
|
|
59
|
+
"""Path 3 exits if agent home doesn't exist."""
|
|
60
|
+
from skcapstone.install_wizard import _path_update_existing
|
|
61
|
+
|
|
62
|
+
fake_home = str(tmp_path / "nonexistent")
|
|
63
|
+
with pytest.raises(SystemExit):
|
|
64
|
+
_path_update_existing(
|
|
65
|
+
home=fake_home,
|
|
66
|
+
skip_deps=True,
|
|
67
|
+
skip_ritual=True,
|
|
68
|
+
)
|