@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,397 @@
|
|
|
1
|
+
"""Tests for encrypted file transfer."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from skcapstone.file_transfer import (
|
|
11
|
+
FileTransfer,
|
|
12
|
+
TransferManifest,
|
|
13
|
+
ChunkInfo,
|
|
14
|
+
TransferStatus,
|
|
15
|
+
CHUNK_SIZE,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def home(tmp_path: Path) -> Path:
|
|
21
|
+
"""Create a minimal agent home for file transfer tests."""
|
|
22
|
+
identity_dir = tmp_path / "identity"
|
|
23
|
+
identity_dir.mkdir()
|
|
24
|
+
(identity_dir / "identity.json").write_text(json.dumps({
|
|
25
|
+
"name": "test-agent",
|
|
26
|
+
"fingerprint": "ABCD1234567890ABCDEF1234567890ABCDEF1234",
|
|
27
|
+
}), encoding="utf-8")
|
|
28
|
+
|
|
29
|
+
security_dir = tmp_path / "security"
|
|
30
|
+
security_dir.mkdir()
|
|
31
|
+
|
|
32
|
+
return tmp_path
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.fixture
|
|
36
|
+
def ft(home: Path) -> FileTransfer:
|
|
37
|
+
"""Create an initialized FileTransfer instance."""
|
|
38
|
+
t = FileTransfer(home, agent_name="opus", chunk_size=1024)
|
|
39
|
+
t.initialize()
|
|
40
|
+
return t
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.fixture
|
|
44
|
+
def sample_file(tmp_path: Path) -> Path:
|
|
45
|
+
"""Create a sample file for transfer."""
|
|
46
|
+
f = tmp_path / "test-document.txt"
|
|
47
|
+
f.write_text("Hello, sovereign world! " * 100, encoding="utf-8")
|
|
48
|
+
return f
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@pytest.fixture
|
|
52
|
+
def large_file(tmp_path: Path) -> Path:
|
|
53
|
+
"""Create a file larger than one chunk."""
|
|
54
|
+
f = tmp_path / "large-file.bin"
|
|
55
|
+
f.write_bytes(b"X" * 3000) # 3 chunks at 1024 bytes
|
|
56
|
+
return f
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# Initialization
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class TestInitialization:
|
|
65
|
+
"""Tests for file transfer setup."""
|
|
66
|
+
|
|
67
|
+
def test_initialize_creates_dirs(self, home: Path) -> None:
|
|
68
|
+
"""Initialize creates directory structure."""
|
|
69
|
+
ft = FileTransfer(home)
|
|
70
|
+
ft.initialize()
|
|
71
|
+
assert (home / "file-transfer" / "outbox").is_dir()
|
|
72
|
+
assert (home / "file-transfer" / "inbox").is_dir()
|
|
73
|
+
assert (home / "file-transfer" / "completed").is_dir()
|
|
74
|
+
|
|
75
|
+
def test_initialize_idempotent(self, ft: FileTransfer, home: Path) -> None:
|
|
76
|
+
"""Multiple initializations don't break anything."""
|
|
77
|
+
ft.initialize()
|
|
78
|
+
ft.initialize()
|
|
79
|
+
assert (home / "file-transfer" / "outbox").is_dir()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# ---------------------------------------------------------------------------
|
|
83
|
+
# Sending
|
|
84
|
+
# ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class TestSend:
|
|
88
|
+
"""Tests for file sending."""
|
|
89
|
+
|
|
90
|
+
def test_send_creates_manifest(
|
|
91
|
+
self, ft: FileTransfer, sample_file: Path, home: Path,
|
|
92
|
+
) -> None:
|
|
93
|
+
"""Send creates a manifest file."""
|
|
94
|
+
manifest = ft.send(sample_file, recipient="lumina")
|
|
95
|
+
manifest_path = home / "file-transfer" / "outbox" / manifest.transfer_id / "manifest.json"
|
|
96
|
+
assert manifest_path.exists()
|
|
97
|
+
|
|
98
|
+
def test_send_creates_chunks(
|
|
99
|
+
self, ft: FileTransfer, sample_file: Path, home: Path,
|
|
100
|
+
) -> None:
|
|
101
|
+
"""Send creates encrypted chunk files."""
|
|
102
|
+
manifest = ft.send(sample_file, recipient="lumina")
|
|
103
|
+
transfer_dir = home / "file-transfer" / "outbox" / manifest.transfer_id
|
|
104
|
+
chunks = list(transfer_dir.glob("chunk-*.enc"))
|
|
105
|
+
assert len(chunks) == manifest.total_chunks
|
|
106
|
+
assert len(chunks) > 0
|
|
107
|
+
|
|
108
|
+
def test_send_manifest_metadata(
|
|
109
|
+
self, ft: FileTransfer, sample_file: Path,
|
|
110
|
+
) -> None:
|
|
111
|
+
"""Manifest contains correct file metadata."""
|
|
112
|
+
manifest = ft.send(sample_file, recipient="lumina")
|
|
113
|
+
assert manifest.filename == "test-document.txt"
|
|
114
|
+
assert manifest.file_size == sample_file.stat().st_size
|
|
115
|
+
assert manifest.sender == "opus"
|
|
116
|
+
assert manifest.recipient == "lumina"
|
|
117
|
+
assert manifest.file_sha256
|
|
118
|
+
assert manifest.total_chunks > 0
|
|
119
|
+
|
|
120
|
+
def test_send_marks_chunks_sent(
|
|
121
|
+
self, ft: FileTransfer, sample_file: Path,
|
|
122
|
+
) -> None:
|
|
123
|
+
"""All chunks are marked as sent."""
|
|
124
|
+
manifest = ft.send(sample_file, recipient="grok")
|
|
125
|
+
assert all(c.sent for c in manifest.chunks)
|
|
126
|
+
|
|
127
|
+
def test_send_multiple_chunks(
|
|
128
|
+
self, ft: FileTransfer, large_file: Path,
|
|
129
|
+
) -> None:
|
|
130
|
+
"""Large files produce multiple chunks."""
|
|
131
|
+
manifest = ft.send(large_file, recipient="lumina")
|
|
132
|
+
assert manifest.total_chunks == 3
|
|
133
|
+
|
|
134
|
+
def test_send_nonexistent_file_raises(self, ft: FileTransfer) -> None:
|
|
135
|
+
"""Sending a nonexistent file raises FileNotFoundError."""
|
|
136
|
+
with pytest.raises(FileNotFoundError):
|
|
137
|
+
ft.send(Path("/nonexistent/file.txt"), recipient="lumina")
|
|
138
|
+
|
|
139
|
+
def test_send_empty_file_raises(self, ft: FileTransfer, tmp_path: Path) -> None:
|
|
140
|
+
"""Sending an empty file raises ValueError."""
|
|
141
|
+
empty = tmp_path / "empty.txt"
|
|
142
|
+
empty.write_bytes(b"")
|
|
143
|
+
with pytest.raises(ValueError, match="empty"):
|
|
144
|
+
ft.send(empty, recipient="lumina")
|
|
145
|
+
|
|
146
|
+
def test_send_without_encryption(
|
|
147
|
+
self, ft: FileTransfer, sample_file: Path,
|
|
148
|
+
) -> None:
|
|
149
|
+
"""Files can be sent without encryption."""
|
|
150
|
+
manifest = ft.send(sample_file, recipient="lumina", encrypt=False)
|
|
151
|
+
assert all(not c.encrypted for c in manifest.chunks)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# ---------------------------------------------------------------------------
|
|
155
|
+
# Receiving
|
|
156
|
+
# ---------------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class TestReceive:
|
|
160
|
+
"""Tests for file receiving."""
|
|
161
|
+
|
|
162
|
+
def test_receive_reassembles_file(
|
|
163
|
+
self, ft: FileTransfer, sample_file: Path, home: Path,
|
|
164
|
+
) -> None:
|
|
165
|
+
"""Receive reassembles the original file."""
|
|
166
|
+
manifest = ft.send(sample_file, recipient="lumina")
|
|
167
|
+
output = ft.receive(manifest.transfer_id)
|
|
168
|
+
assert output.exists()
|
|
169
|
+
assert output.read_bytes() == sample_file.read_bytes()
|
|
170
|
+
|
|
171
|
+
def test_receive_large_file(
|
|
172
|
+
self, ft: FileTransfer, large_file: Path,
|
|
173
|
+
) -> None:
|
|
174
|
+
"""Receive handles multi-chunk files."""
|
|
175
|
+
manifest = ft.send(large_file, recipient="lumina")
|
|
176
|
+
output = ft.receive(manifest.transfer_id)
|
|
177
|
+
assert output.read_bytes() == large_file.read_bytes()
|
|
178
|
+
|
|
179
|
+
def test_receive_records_completion(
|
|
180
|
+
self, ft: FileTransfer, sample_file: Path, home: Path,
|
|
181
|
+
) -> None:
|
|
182
|
+
"""Receive writes a completion receipt."""
|
|
183
|
+
manifest = ft.send(sample_file, recipient="lumina")
|
|
184
|
+
ft.receive(manifest.transfer_id)
|
|
185
|
+
receipt = home / "file-transfer" / "completed" / f"{manifest.transfer_id}.json"
|
|
186
|
+
assert receipt.exists()
|
|
187
|
+
|
|
188
|
+
def test_receive_custom_output_dir(
|
|
189
|
+
self, ft: FileTransfer, sample_file: Path, tmp_path: Path,
|
|
190
|
+
) -> None:
|
|
191
|
+
"""Receive writes to custom output directory."""
|
|
192
|
+
manifest = ft.send(sample_file, recipient="lumina")
|
|
193
|
+
output_dir = tmp_path / "downloads"
|
|
194
|
+
output = ft.receive(manifest.transfer_id, output_dir=output_dir)
|
|
195
|
+
assert output.parent == output_dir
|
|
196
|
+
assert output.read_bytes() == sample_file.read_bytes()
|
|
197
|
+
|
|
198
|
+
def test_receive_nonexistent_transfer_raises(self, ft: FileTransfer) -> None:
|
|
199
|
+
"""Receiving a nonexistent transfer raises FileNotFoundError."""
|
|
200
|
+
with pytest.raises(FileNotFoundError):
|
|
201
|
+
ft.receive("nonexistent-id")
|
|
202
|
+
|
|
203
|
+
def test_receive_detects_tampering(
|
|
204
|
+
self, ft: FileTransfer, sample_file: Path, home: Path,
|
|
205
|
+
) -> None:
|
|
206
|
+
"""Receive detects tampered chunks."""
|
|
207
|
+
manifest = ft.send(sample_file, recipient="lumina", encrypt=False)
|
|
208
|
+
# Tamper with a chunk
|
|
209
|
+
transfer_dir = home / "file-transfer" / "outbox" / manifest.transfer_id
|
|
210
|
+
chunk_file = list(transfer_dir.glob("chunk-*.enc"))[0]
|
|
211
|
+
chunk_file.write_bytes(b"TAMPERED DATA")
|
|
212
|
+
|
|
213
|
+
with pytest.raises(ValueError, match="integrity"):
|
|
214
|
+
ft.receive(manifest.transfer_id)
|
|
215
|
+
|
|
216
|
+
def test_receive_without_encryption(
|
|
217
|
+
self, ft: FileTransfer, sample_file: Path,
|
|
218
|
+
) -> None:
|
|
219
|
+
"""Unencrypted transfers reassemble correctly."""
|
|
220
|
+
manifest = ft.send(sample_file, recipient="lumina", encrypt=False)
|
|
221
|
+
output = ft.receive(manifest.transfer_id)
|
|
222
|
+
assert output.read_bytes() == sample_file.read_bytes()
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
# ---------------------------------------------------------------------------
|
|
226
|
+
# Resume
|
|
227
|
+
# ---------------------------------------------------------------------------
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class TestResume:
|
|
231
|
+
"""Tests for transfer resume functionality."""
|
|
232
|
+
|
|
233
|
+
def test_resume_send_complete(
|
|
234
|
+
self, ft: FileTransfer, sample_file: Path,
|
|
235
|
+
) -> None:
|
|
236
|
+
"""Complete transfer has no unsent chunks."""
|
|
237
|
+
manifest = ft.send(sample_file, recipient="lumina")
|
|
238
|
+
unsent = ft.resume_send(manifest.transfer_id)
|
|
239
|
+
assert unsent == []
|
|
240
|
+
|
|
241
|
+
def test_resume_receive_all_present(
|
|
242
|
+
self, ft: FileTransfer, sample_file: Path,
|
|
243
|
+
) -> None:
|
|
244
|
+
"""Transfer with all chunks has no missing."""
|
|
245
|
+
manifest = ft.send(sample_file, recipient="lumina")
|
|
246
|
+
missing = ft.resume_receive(manifest.transfer_id)
|
|
247
|
+
assert missing == []
|
|
248
|
+
|
|
249
|
+
def test_resume_receive_missing_chunks(
|
|
250
|
+
self, ft: FileTransfer, large_file: Path, home: Path,
|
|
251
|
+
) -> None:
|
|
252
|
+
"""Detect missing chunks for resume."""
|
|
253
|
+
manifest = ft.send(large_file, recipient="lumina")
|
|
254
|
+
# Delete a chunk to simulate interrupted transfer
|
|
255
|
+
transfer_dir = home / "file-transfer" / "outbox" / manifest.transfer_id
|
|
256
|
+
(transfer_dir / "chunk-0001.enc").unlink()
|
|
257
|
+
|
|
258
|
+
missing = ft.resume_receive(manifest.transfer_id)
|
|
259
|
+
assert 1 in missing
|
|
260
|
+
|
|
261
|
+
def test_resume_nonexistent_transfer(self, ft: FileTransfer) -> None:
|
|
262
|
+
"""Resume on nonexistent transfer returns empty."""
|
|
263
|
+
assert ft.resume_send("ghost") == []
|
|
264
|
+
assert ft.resume_receive("ghost") == []
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
# ---------------------------------------------------------------------------
|
|
268
|
+
# Listing
|
|
269
|
+
# ---------------------------------------------------------------------------
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class TestListTransfers:
|
|
273
|
+
"""Tests for transfer listing."""
|
|
274
|
+
|
|
275
|
+
def test_list_empty(self, ft: FileTransfer) -> None:
|
|
276
|
+
"""Empty transfer system returns no items."""
|
|
277
|
+
assert ft.list_transfers() == []
|
|
278
|
+
|
|
279
|
+
def test_list_after_send(
|
|
280
|
+
self, ft: FileTransfer, sample_file: Path,
|
|
281
|
+
) -> None:
|
|
282
|
+
"""Sent transfers appear in listing."""
|
|
283
|
+
ft.send(sample_file, recipient="lumina")
|
|
284
|
+
transfers = ft.list_transfers()
|
|
285
|
+
assert len(transfers) == 1
|
|
286
|
+
assert transfers[0].direction == "send"
|
|
287
|
+
assert transfers[0].filename == "test-document.txt"
|
|
288
|
+
|
|
289
|
+
def test_list_filter_direction(
|
|
290
|
+
self, ft: FileTransfer, sample_file: Path,
|
|
291
|
+
) -> None:
|
|
292
|
+
"""Can filter by send/receive direction."""
|
|
293
|
+
ft.send(sample_file, recipient="lumina")
|
|
294
|
+
assert len(ft.list_transfers(direction="send")) == 1
|
|
295
|
+
assert len(ft.list_transfers(direction="receive")) == 0
|
|
296
|
+
|
|
297
|
+
def test_list_progress(
|
|
298
|
+
self, ft: FileTransfer, sample_file: Path,
|
|
299
|
+
) -> None:
|
|
300
|
+
"""Transfer status shows progress."""
|
|
301
|
+
ft.send(sample_file, recipient="lumina")
|
|
302
|
+
status = ft.list_transfers()[0]
|
|
303
|
+
assert status.progress == 1.0
|
|
304
|
+
assert status.chunks_done == status.total_chunks
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# ---------------------------------------------------------------------------
|
|
308
|
+
# Manifest and status
|
|
309
|
+
# ---------------------------------------------------------------------------
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
class TestManifestAndStatus:
|
|
313
|
+
"""Tests for manifest retrieval and status."""
|
|
314
|
+
|
|
315
|
+
def test_get_manifest(
|
|
316
|
+
self, ft: FileTransfer, sample_file: Path,
|
|
317
|
+
) -> None:
|
|
318
|
+
"""Get manifest by transfer ID."""
|
|
319
|
+
manifest = ft.send(sample_file, recipient="lumina")
|
|
320
|
+
retrieved = ft.get_manifest(manifest.transfer_id)
|
|
321
|
+
assert retrieved is not None
|
|
322
|
+
assert retrieved.filename == manifest.filename
|
|
323
|
+
|
|
324
|
+
def test_get_manifest_nonexistent(self, ft: FileTransfer) -> None:
|
|
325
|
+
"""Getting nonexistent manifest returns None."""
|
|
326
|
+
assert ft.get_manifest("ghost") is None
|
|
327
|
+
|
|
328
|
+
def test_status_summary(self, ft: FileTransfer, sample_file: Path) -> None:
|
|
329
|
+
"""Status returns structured summary."""
|
|
330
|
+
ft.send(sample_file, recipient="lumina")
|
|
331
|
+
status = ft.status()
|
|
332
|
+
assert status["outbox_transfers"] == 1
|
|
333
|
+
assert status["inbox_transfers"] == 0
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
# ---------------------------------------------------------------------------
|
|
337
|
+
# Cleanup
|
|
338
|
+
# ---------------------------------------------------------------------------
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
class TestCleanup:
|
|
342
|
+
"""Tests for transfer cleanup."""
|
|
343
|
+
|
|
344
|
+
def test_cleanup_removes_files(
|
|
345
|
+
self, ft: FileTransfer, sample_file: Path, home: Path,
|
|
346
|
+
) -> None:
|
|
347
|
+
"""Cleanup removes transfer directory."""
|
|
348
|
+
manifest = ft.send(sample_file, recipient="lumina")
|
|
349
|
+
assert ft.cleanup(manifest.transfer_id) is True
|
|
350
|
+
transfer_dir = home / "file-transfer" / "outbox" / manifest.transfer_id
|
|
351
|
+
assert not transfer_dir.exists()
|
|
352
|
+
|
|
353
|
+
def test_cleanup_nonexistent(self, ft: FileTransfer) -> None:
|
|
354
|
+
"""Cleaning up nonexistent transfer returns False."""
|
|
355
|
+
assert ft.cleanup("ghost") is False
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
# ---------------------------------------------------------------------------
|
|
359
|
+
# Model tests
|
|
360
|
+
# ---------------------------------------------------------------------------
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class TestModels:
|
|
364
|
+
"""Tests for transfer models."""
|
|
365
|
+
|
|
366
|
+
def test_manifest_progress_empty(self) -> None:
|
|
367
|
+
"""Empty manifest has zero progress."""
|
|
368
|
+
m = TransferManifest(
|
|
369
|
+
filename="test", file_size=100,
|
|
370
|
+
file_sha256="abc", total_chunks=0,
|
|
371
|
+
)
|
|
372
|
+
assert m.progress == 0.0
|
|
373
|
+
|
|
374
|
+
def test_manifest_is_complete(self) -> None:
|
|
375
|
+
"""Complete manifest detected correctly."""
|
|
376
|
+
m = TransferManifest(
|
|
377
|
+
filename="test", file_size=100,
|
|
378
|
+
file_sha256="abc", total_chunks=1,
|
|
379
|
+
chunks=[ChunkInfo(index=0, size=100, sha256="def", sent=True, received=True)],
|
|
380
|
+
)
|
|
381
|
+
assert m.is_complete is True
|
|
382
|
+
|
|
383
|
+
def test_manifest_not_complete(self) -> None:
|
|
384
|
+
"""Incomplete manifest detected correctly."""
|
|
385
|
+
m = TransferManifest(
|
|
386
|
+
filename="test", file_size=100,
|
|
387
|
+
file_sha256="abc", total_chunks=1,
|
|
388
|
+
chunks=[ChunkInfo(index=0, size=100, sha256="def", sent=True, received=False)],
|
|
389
|
+
)
|
|
390
|
+
assert m.is_complete is False
|
|
391
|
+
|
|
392
|
+
def test_chunk_info_defaults(self) -> None:
|
|
393
|
+
"""ChunkInfo has sensible defaults."""
|
|
394
|
+
c = ChunkInfo(index=0, size=1024, sha256="abc")
|
|
395
|
+
assert c.encrypted is True
|
|
396
|
+
assert c.sent is False
|
|
397
|
+
assert c.received is False
|