@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,301 @@
|
|
|
1
|
+
"""Sync commands: push, pull, status, setup, pair, export-pubkey, import-peer-key."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
from ._common import AGENT_HOME, console, status_icon, logger
|
|
13
|
+
from ._validators import validate_file_path
|
|
14
|
+
from ..pillars.security import audit_event
|
|
15
|
+
from ..pillars.sync import discover_sync, push_seed, pull_seeds, save_sync_state
|
|
16
|
+
from ..runtime import get_runtime
|
|
17
|
+
|
|
18
|
+
from rich.panel import Panel
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _register_peer_fingerprint(home_path: Path, fingerprint: str) -> None:
|
|
22
|
+
import yaml as _yaml
|
|
23
|
+
|
|
24
|
+
config_file = home_path / "config" / "config.yaml"
|
|
25
|
+
if not config_file.exists():
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
data = _yaml.safe_load(config_file.read_text(encoding="utf-8")) or {}
|
|
30
|
+
sync_data = data.setdefault("sync", {})
|
|
31
|
+
peers = sync_data.setdefault("peer_fingerprints", [])
|
|
32
|
+
if fingerprint not in peers:
|
|
33
|
+
peers.append(fingerprint)
|
|
34
|
+
config_file.write_text(_yaml.dump(data, default_flow_style=False), encoding="utf-8")
|
|
35
|
+
logger.info("Registered peer fingerprint: %s", fingerprint)
|
|
36
|
+
except Exception as exc:
|
|
37
|
+
logger.warning("Could not persist peer fingerprint: %s", exc)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def register_sync_commands(main: click.Group) -> None:
|
|
41
|
+
"""Register the sync command group."""
|
|
42
|
+
|
|
43
|
+
@main.group()
|
|
44
|
+
def sync():
|
|
45
|
+
"""Sovereign Singularity — encrypted memory sync.
|
|
46
|
+
|
|
47
|
+
Push your agent's state to the mesh. Pull from peers.
|
|
48
|
+
GPG-encrypted, Syncthing-transported, truly sovereign.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
@sync.command("push")
|
|
52
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
53
|
+
@click.option("--no-encrypt", is_flag=True, help="Skip GPG encryption.")
|
|
54
|
+
def sync_push(home, no_encrypt):
|
|
55
|
+
"""Push current agent state to the sync mesh."""
|
|
56
|
+
home_path = Path(home).expanduser()
|
|
57
|
+
if not home_path.exists():
|
|
58
|
+
console.print("[bold red]No agent found.[/] Run skcapstone init first.")
|
|
59
|
+
sys.exit(1)
|
|
60
|
+
|
|
61
|
+
runtime = get_runtime(home_path)
|
|
62
|
+
name = runtime.manifest.name
|
|
63
|
+
|
|
64
|
+
console.print(f"\n Collecting seed for [cyan]{name}[/]...", end=" ")
|
|
65
|
+
result = push_seed(home_path, name, encrypt=not no_encrypt)
|
|
66
|
+
|
|
67
|
+
if result:
|
|
68
|
+
console.print("[green]done[/]")
|
|
69
|
+
console.print(f" [dim]Seed: {result.name}[/]")
|
|
70
|
+
is_encrypted = result.suffix == ".gpg"
|
|
71
|
+
if is_encrypted:
|
|
72
|
+
console.print(" [green]GPG encrypted[/]")
|
|
73
|
+
else:
|
|
74
|
+
console.print(" [yellow]Plaintext (no GPG)[/]")
|
|
75
|
+
|
|
76
|
+
sync_st = discover_sync(home_path)
|
|
77
|
+
sync_st.last_push = datetime.now(timezone.utc)
|
|
78
|
+
sync_st.seed_count = sync_st.seed_count + 1
|
|
79
|
+
save_sync_state(home_path / "sync", sync_st)
|
|
80
|
+
|
|
81
|
+
audit_event(home_path, "SYNC_PUSH", f"Seed pushed: {result.name}")
|
|
82
|
+
console.print(" [dim]Syncthing will propagate to all peers.[/]\n")
|
|
83
|
+
else:
|
|
84
|
+
console.print("[red]failed[/]")
|
|
85
|
+
sys.exit(1)
|
|
86
|
+
|
|
87
|
+
@sync.command("pull")
|
|
88
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
89
|
+
@click.option("--no-decrypt", is_flag=True, help="Skip GPG decryption.")
|
|
90
|
+
def sync_pull(home, no_decrypt):
|
|
91
|
+
"""Pull and process seed files from peers."""
|
|
92
|
+
home_path = Path(home).expanduser()
|
|
93
|
+
if not home_path.exists():
|
|
94
|
+
console.print("[bold red]No agent found.[/] Run skcapstone init first.")
|
|
95
|
+
sys.exit(1)
|
|
96
|
+
|
|
97
|
+
console.print("\n Pulling seeds from inbox...", end=" ")
|
|
98
|
+
seeds = pull_seeds(home_path, decrypt=not no_decrypt)
|
|
99
|
+
|
|
100
|
+
if not seeds:
|
|
101
|
+
console.print("[yellow]no new seeds[/]\n")
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
console.print(f"[green]{len(seeds)} seed(s) received[/]")
|
|
105
|
+
for s in seeds:
|
|
106
|
+
source = s.get("source_host", "unknown")
|
|
107
|
+
agent = s.get("agent_name", "unknown")
|
|
108
|
+
created = s.get("created_at", "unknown")
|
|
109
|
+
console.print(f" [cyan]{agent}[/]@{source} [{created}]")
|
|
110
|
+
|
|
111
|
+
sync_st = discover_sync(home_path)
|
|
112
|
+
sync_st.last_pull = datetime.now(timezone.utc)
|
|
113
|
+
save_sync_state(home_path / "sync", sync_st)
|
|
114
|
+
|
|
115
|
+
audit_event(home_path, "SYNC_PULL", f"Pulled {len(seeds)} seed(s)")
|
|
116
|
+
console.print()
|
|
117
|
+
|
|
118
|
+
@sync.command("status")
|
|
119
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
120
|
+
def sync_status(home):
|
|
121
|
+
"""Show sync layer status and recent activity."""
|
|
122
|
+
home_path = Path(home).expanduser()
|
|
123
|
+
state = discover_sync(home_path)
|
|
124
|
+
|
|
125
|
+
console.print()
|
|
126
|
+
console.print(
|
|
127
|
+
Panel(
|
|
128
|
+
f"Transport: [cyan]{state.transport.value}[/]\n"
|
|
129
|
+
f"Status: {status_icon(state.status)}\n"
|
|
130
|
+
f"Seeds: [bold]{state.seed_count}[/]\n"
|
|
131
|
+
f"GPG Key: {state.gpg_fingerprint or '[yellow]none[/]'}\n"
|
|
132
|
+
f"Last Push: {state.last_push or '[dim]never[/]'}\n"
|
|
133
|
+
f"Last Pull: {state.last_pull or '[dim]never[/]'}\n"
|
|
134
|
+
f"Peers: {state.peers_known}",
|
|
135
|
+
title="Sovereign Singularity",
|
|
136
|
+
border_style="magenta",
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
sync_dir = home_path / "sync"
|
|
141
|
+
for folder_name in ("outbox", "inbox", "archive"):
|
|
142
|
+
d = sync_dir / folder_name
|
|
143
|
+
if d.exists():
|
|
144
|
+
count = sum(1 for f in d.iterdir() if not f.name.startswith("."))
|
|
145
|
+
console.print(f" {folder_name}: {count} file(s)")
|
|
146
|
+
|
|
147
|
+
console.print()
|
|
148
|
+
|
|
149
|
+
@sync.command("setup")
|
|
150
|
+
def sync_setup():
|
|
151
|
+
"""Set up Syncthing for sovereign P2P memory sync."""
|
|
152
|
+
from ..skills.syncthing_setup import full_setup
|
|
153
|
+
|
|
154
|
+
console.print("\n [bold cyan]Syncthing Setup[/bold cyan]\n")
|
|
155
|
+
result = full_setup()
|
|
156
|
+
|
|
157
|
+
if not result["syncthing_installed"]:
|
|
158
|
+
console.print("[yellow]Syncthing is not installed.[/yellow]\n")
|
|
159
|
+
console.print(result["install_instructions"])
|
|
160
|
+
console.print("\nAfter installing, run [cyan]skcapstone sync setup[/cyan] again.")
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
console.print("[green]Syncthing detected[/green]")
|
|
164
|
+
|
|
165
|
+
if result["folder_configured"]:
|
|
166
|
+
console.print(f" Shared folder: [cyan]{result['folder_path']}[/cyan]")
|
|
167
|
+
else:
|
|
168
|
+
console.print(" [yellow]Could not configure shared folder automatically.[/]")
|
|
169
|
+
|
|
170
|
+
if result["started"]:
|
|
171
|
+
console.print(" [green]Syncthing started[/green]")
|
|
172
|
+
else:
|
|
173
|
+
console.print(" [yellow]Could not start Syncthing automatically.[/]")
|
|
174
|
+
|
|
175
|
+
if result["device_id"]:
|
|
176
|
+
console.print(f"\n [bold]Your Device ID:[/bold]")
|
|
177
|
+
console.print(f" [cyan]{result['device_id']}[/cyan]")
|
|
178
|
+
console.print("\n Share this ID with your other device to pair.")
|
|
179
|
+
console.print(f" On the other device: [cyan]skcapstone sync pair {result['device_id']}[/cyan]")
|
|
180
|
+
|
|
181
|
+
if result["qr_code"]:
|
|
182
|
+
console.print("\n [bold]QR Code:[/bold]")
|
|
183
|
+
console.print(result["qr_code"])
|
|
184
|
+
else:
|
|
185
|
+
console.print("\n [dim]Install 'qrcode' for QR output: pip install qrcode[/dim]")
|
|
186
|
+
else:
|
|
187
|
+
console.print(" [yellow]Could not retrieve device ID. Syncthing may still be starting.[/]")
|
|
188
|
+
|
|
189
|
+
console.print()
|
|
190
|
+
|
|
191
|
+
@sync.command("pair")
|
|
192
|
+
@click.argument("device_id")
|
|
193
|
+
@click.option("--name", "-n", default="peer", help="Friendly name for the device.")
|
|
194
|
+
def sync_pair(device_id, name):
|
|
195
|
+
"""Add a remote device for P2P sync pairing."""
|
|
196
|
+
from ..skills.syncthing_setup import add_remote_device, detect_syncthing
|
|
197
|
+
|
|
198
|
+
if not detect_syncthing():
|
|
199
|
+
console.print("[red]Syncthing not installed.[/] Run [cyan]skcapstone sync setup[/cyan] first.")
|
|
200
|
+
sys.exit(1)
|
|
201
|
+
|
|
202
|
+
console.print(f"\n Adding device [cyan]{name}[/cyan]...")
|
|
203
|
+
if add_remote_device(device_id, name):
|
|
204
|
+
console.print(f" [green]Device paired![/green]")
|
|
205
|
+
console.print(f" Device ID: [dim]{device_id[:20]}...[/dim]")
|
|
206
|
+
console.print(" The skcapstone-sync folder is now shared with this device.")
|
|
207
|
+
else:
|
|
208
|
+
console.print(" [red]Failed to add device.[/] Make sure Syncthing is running.")
|
|
209
|
+
console.print()
|
|
210
|
+
|
|
211
|
+
@sync.command("export-pubkey")
|
|
212
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path(), help="Agent home directory.")
|
|
213
|
+
@click.option("--output", "-o", default=None, help="Output file path (default: stdout).")
|
|
214
|
+
def sync_export_pubkey(home, output):
|
|
215
|
+
"""Export your GPG public key for sharing with peers."""
|
|
216
|
+
import shutil
|
|
217
|
+
import subprocess as sp
|
|
218
|
+
|
|
219
|
+
home_path = Path(home).expanduser()
|
|
220
|
+
|
|
221
|
+
if not shutil.which("gpg"):
|
|
222
|
+
console.print("[red]gpg not found in PATH.[/]")
|
|
223
|
+
sys.exit(1)
|
|
224
|
+
|
|
225
|
+
from ..pillars.sync import _detect_gpg_key
|
|
226
|
+
fingerprint = _detect_gpg_key(home_path)
|
|
227
|
+
if not fingerprint:
|
|
228
|
+
console.print("[red]No GPG key found.[/] Run [cyan]skcapstone init[/cyan] to generate.")
|
|
229
|
+
sys.exit(1)
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
result = sp.run(["gpg", "--armor", "--export", fingerprint],
|
|
233
|
+
capture_output=True, check=True, timeout=15)
|
|
234
|
+
pubkey_data = result.stdout
|
|
235
|
+
except sp.CalledProcessError as exc:
|
|
236
|
+
console.print(f"[red]GPG export failed:[/] {exc}")
|
|
237
|
+
sys.exit(1)
|
|
238
|
+
|
|
239
|
+
if output:
|
|
240
|
+
Path(output).write_bytes(pubkey_data)
|
|
241
|
+
console.print(f" [green]Public key exported to:[/] {output}")
|
|
242
|
+
console.print(f" [dim]Fingerprint: {fingerprint}[/]")
|
|
243
|
+
console.print(f" Share this file with your peer. They import it with: "
|
|
244
|
+
f"[cyan]skcapstone sync import-peer-key --file {output}[/cyan]")
|
|
245
|
+
else:
|
|
246
|
+
console.print(pubkey_data.decode("utf-8", errors="replace"))
|
|
247
|
+
|
|
248
|
+
@sync.command("import-peer-key")
|
|
249
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path(), help="Agent home directory.")
|
|
250
|
+
@click.option("--file", "-f", "keyfile", required=True, help="Path to peer's exported public key.")
|
|
251
|
+
@click.option("--fingerprint", default=None, help="Expected fingerprint (for verification).")
|
|
252
|
+
def sync_import_peer_key(home, keyfile, fingerprint):
|
|
253
|
+
"""Import a peer's GPG public key and register it for encrypted sync."""
|
|
254
|
+
import shutil
|
|
255
|
+
import subprocess as sp
|
|
256
|
+
|
|
257
|
+
validate_file_path(keyfile)
|
|
258
|
+
|
|
259
|
+
home_path = Path(home).expanduser()
|
|
260
|
+
key_path = Path(keyfile).expanduser()
|
|
261
|
+
|
|
262
|
+
if not shutil.which("gpg"):
|
|
263
|
+
console.print("[red]gpg not found in PATH.[/]")
|
|
264
|
+
sys.exit(1)
|
|
265
|
+
|
|
266
|
+
if not key_path.exists():
|
|
267
|
+
console.print(f"[red]Key file not found:[/] {key_path}")
|
|
268
|
+
sys.exit(1)
|
|
269
|
+
|
|
270
|
+
try:
|
|
271
|
+
result = sp.run(["gpg", "--import", str(key_path)],
|
|
272
|
+
capture_output=True, text=True, timeout=15)
|
|
273
|
+
if result.returncode != 0:
|
|
274
|
+
console.print(f"[red]GPG import failed:[/] {result.stderr.strip()}")
|
|
275
|
+
sys.exit(1)
|
|
276
|
+
except sp.CalledProcessError as exc:
|
|
277
|
+
console.print(f"[red]GPG import error:[/] {exc}")
|
|
278
|
+
sys.exit(1)
|
|
279
|
+
|
|
280
|
+
imported_fp: Optional[str] = None
|
|
281
|
+
for line in result.stderr.splitlines():
|
|
282
|
+
if "key" in line.lower() and ":" in line:
|
|
283
|
+
parts = line.split(":")
|
|
284
|
+
for part in parts:
|
|
285
|
+
part = part.strip().replace(" ", "")
|
|
286
|
+
if len(part) in (8, 16, 40) and all(c in "0123456789ABCDEFabcdef" for c in part):
|
|
287
|
+
imported_fp = part.upper()
|
|
288
|
+
break
|
|
289
|
+
if imported_fp:
|
|
290
|
+
break
|
|
291
|
+
|
|
292
|
+
if fingerprint and imported_fp and fingerprint.upper() != imported_fp:
|
|
293
|
+
console.print(f"[yellow]Warning: expected fingerprint {fingerprint} but got {imported_fp}[/]")
|
|
294
|
+
|
|
295
|
+
if imported_fp:
|
|
296
|
+
_register_peer_fingerprint(home_path, imported_fp)
|
|
297
|
+
console.print(f" [green]Peer key imported:[/] {imported_fp}")
|
|
298
|
+
console.print(" Future [cyan]skcapstone sync push[/cyan] will encrypt to this peer.")
|
|
299
|
+
else:
|
|
300
|
+
console.print("[yellow]Key imported into GPG but fingerprint could not be parsed.[/]")
|
|
301
|
+
console.print(" Run: [dim]gpg --list-keys[/dim] to find it, then add manually.")
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""Telegram integration CLI commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
from ._common import AGENT_HOME, console
|
|
13
|
+
|
|
14
|
+
from rich.panel import Panel
|
|
15
|
+
from rich.table import Table
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def register_telegram_commands(main: click.Group) -> None:
|
|
19
|
+
"""Register the telegram command group."""
|
|
20
|
+
|
|
21
|
+
@main.group()
|
|
22
|
+
def telegram():
|
|
23
|
+
"""Telegram integration — send, poll, list chats, check setup.
|
|
24
|
+
|
|
25
|
+
Requires TELEGRAM_API_ID and TELEGRAM_API_HASH environment variables.
|
|
26
|
+
Install Telethon with: pip install skmemory[telegram]
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
@telegram.command("setup")
|
|
30
|
+
def telegram_setup():
|
|
31
|
+
"""Check Telegram API setup status.
|
|
32
|
+
|
|
33
|
+
Reports whether Telethon is installed, API credentials are set,
|
|
34
|
+
and a session file exists.
|
|
35
|
+
"""
|
|
36
|
+
try:
|
|
37
|
+
from skmemory.importers.telegram_api import check_setup
|
|
38
|
+
except ImportError:
|
|
39
|
+
console.print("[red]skmemory not available.[/] Install with: pip install skmemory[telegram]")
|
|
40
|
+
raise SystemExit(1)
|
|
41
|
+
|
|
42
|
+
result = check_setup()
|
|
43
|
+
|
|
44
|
+
status_icon = "[green]READY[/]" if result["ready"] else "[red]NOT READY[/]"
|
|
45
|
+
lines = [f"Status: {status_icon}"]
|
|
46
|
+
lines.append(f"Telethon installed: {'[green]yes[/]' if result['telethon'] else '[red]no[/]'}")
|
|
47
|
+
lines.append(f"Credentials set: {'[green]yes[/]' if result['credentials'] else '[red]no[/]'}")
|
|
48
|
+
lines.append(f"Session file: {'[green]yes[/]' if result['session'] else '[yellow]no (first run will prompt)[/]'}")
|
|
49
|
+
|
|
50
|
+
if result["messages"]:
|
|
51
|
+
lines.append("")
|
|
52
|
+
lines.append("[bold]Action items:[/]")
|
|
53
|
+
for msg in result["messages"]:
|
|
54
|
+
lines.append(f" [yellow]>[/] {msg}")
|
|
55
|
+
|
|
56
|
+
console.print(Panel("\n".join(lines), title="Telegram Setup", border_style="cyan"))
|
|
57
|
+
|
|
58
|
+
@telegram.command("send")
|
|
59
|
+
@click.argument("chat")
|
|
60
|
+
@click.argument("message")
|
|
61
|
+
@click.option("--parse-mode", "-p", type=click.Choice(["html", "markdown"]),
|
|
62
|
+
help="Message parse mode.")
|
|
63
|
+
def telegram_send(chat, message, parse_mode):
|
|
64
|
+
"""Send a message to a Telegram chat.
|
|
65
|
+
|
|
66
|
+
Example: skcapstone telegram send @username "Hello there!"
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
from skmemory.importers.telegram_api import send_message
|
|
70
|
+
except ImportError:
|
|
71
|
+
console.print("[red]skmemory[telegram] not available.[/] Install with: pip install skmemory[telegram]")
|
|
72
|
+
raise SystemExit(1)
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
result = asyncio.run(send_message(chat, message, parse_mode))
|
|
76
|
+
console.print(Panel(
|
|
77
|
+
f"[green]Sent![/]\n"
|
|
78
|
+
f"Chat: [cyan]{result['chat']}[/]\n"
|
|
79
|
+
f"Message ID: [dim]{result['message_id']}[/]\n"
|
|
80
|
+
f"Date: {result['date']}",
|
|
81
|
+
title="Telegram Send",
|
|
82
|
+
border_style="green",
|
|
83
|
+
))
|
|
84
|
+
except Exception as e:
|
|
85
|
+
console.print(f"[red]Error:[/] {e}")
|
|
86
|
+
raise SystemExit(1)
|
|
87
|
+
|
|
88
|
+
@telegram.command("poll")
|
|
89
|
+
@click.argument("chat")
|
|
90
|
+
@click.option("--limit", "-l", default=20, type=int, help="Max messages to fetch.")
|
|
91
|
+
@click.option("--since", "-s", default=None, help="Only messages after this date (YYYY-MM-DD).")
|
|
92
|
+
def telegram_poll(chat, limit, since):
|
|
93
|
+
"""Fetch recent messages from a Telegram chat.
|
|
94
|
+
|
|
95
|
+
Example: skcapstone telegram poll @channel --limit 10
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
from skmemory.importers.telegram_api import poll_messages
|
|
99
|
+
except ImportError:
|
|
100
|
+
console.print("[red]skmemory[telegram] not available.[/] Install with: pip install skmemory[telegram]")
|
|
101
|
+
raise SystemExit(1)
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
messages = asyncio.run(poll_messages(chat, limit=limit, since=since))
|
|
105
|
+
|
|
106
|
+
if not messages:
|
|
107
|
+
console.print(f"[dim]No messages found in {chat}.[/]")
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
table = Table(title=f"Messages from {chat} ({len(messages)} shown)")
|
|
111
|
+
table.add_column("ID", style="dim", width=10)
|
|
112
|
+
table.add_column("Date", width=20)
|
|
113
|
+
table.add_column("Sender", style="cyan", width=20)
|
|
114
|
+
table.add_column("Text", no_wrap=False)
|
|
115
|
+
|
|
116
|
+
for msg in messages:
|
|
117
|
+
text = msg["text"][:120] + ("..." if len(msg["text"]) > 120 else "")
|
|
118
|
+
table.add_row(
|
|
119
|
+
str(msg["id"]),
|
|
120
|
+
msg["date"][:19] if msg["date"] else "",
|
|
121
|
+
msg["sender"],
|
|
122
|
+
text,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
console.print(table)
|
|
126
|
+
except Exception as e:
|
|
127
|
+
console.print(f"[red]Error:[/] {e}")
|
|
128
|
+
raise SystemExit(1)
|
|
129
|
+
|
|
130
|
+
@telegram.command("catchup")
|
|
131
|
+
@click.argument("chat")
|
|
132
|
+
@click.option("--limit", "-l", default=2000, type=int, help="Max messages to fetch.")
|
|
133
|
+
@click.option("--since", "-s", default=None, help="Only messages after this date (YYYY-MM-DD).")
|
|
134
|
+
@click.option("--min-length", "-m", default=20, type=int, help="Skip messages shorter than this.")
|
|
135
|
+
@click.option("--tags", "-t", default=None, help="Extra comma-separated tags.")
|
|
136
|
+
def telegram_catchup(chat, limit, since, min_length, tags):
|
|
137
|
+
"""Full catch-up import from a Telegram group into all memory tiers.
|
|
138
|
+
|
|
139
|
+
Downloads chat via Telethon and distributes messages by age:
|
|
140
|
+
last 24h → short-term, last 7 days → mid-term, older → long-term.
|
|
141
|
+
|
|
142
|
+
Example: skcapstone telegram catchup @mygroup --limit 500
|
|
143
|
+
"""
|
|
144
|
+
try:
|
|
145
|
+
from skmemory.importers.telegram_api import import_telegram_api
|
|
146
|
+
from skmemory.store import MemoryStore
|
|
147
|
+
except ImportError:
|
|
148
|
+
console.print("[red]skmemory[telegram] not available.[/] Install with: pip install skmemory[telegram]")
|
|
149
|
+
raise SystemExit(1)
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
tags_list = [t.strip() for t in tags.split(",") if t.strip()] if tags else None
|
|
153
|
+
store = MemoryStore()
|
|
154
|
+
stats = import_telegram_api(
|
|
155
|
+
store,
|
|
156
|
+
chat,
|
|
157
|
+
mode="catchup",
|
|
158
|
+
limit=limit,
|
|
159
|
+
since=since,
|
|
160
|
+
min_message_length=min_length,
|
|
161
|
+
tags=tags_list,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
lines = [f"[green]Catch-up complete![/]"]
|
|
165
|
+
for key, val in stats.items():
|
|
166
|
+
lines.append(f" {key}: [cyan]{val}[/]")
|
|
167
|
+
|
|
168
|
+
console.print(Panel("\n".join(lines), title="Telegram Catch-Up", border_style="green"))
|
|
169
|
+
except Exception as e:
|
|
170
|
+
console.print(f"[red]Error:[/] {e}")
|
|
171
|
+
raise SystemExit(1)
|
|
172
|
+
|
|
173
|
+
@telegram.command("chats")
|
|
174
|
+
@click.option("--limit", "-l", default=50, type=int, help="Max chats to list.")
|
|
175
|
+
def telegram_chats(limit):
|
|
176
|
+
"""List available Telegram chats, groups, and channels.
|
|
177
|
+
|
|
178
|
+
Example: skcapstone telegram chats --limit 20
|
|
179
|
+
"""
|
|
180
|
+
try:
|
|
181
|
+
from skmemory.importers.telegram_api import list_chats
|
|
182
|
+
except ImportError:
|
|
183
|
+
console.print("[red]skmemory[telegram] not available.[/] Install with: pip install skmemory[telegram]")
|
|
184
|
+
raise SystemExit(1)
|
|
185
|
+
|
|
186
|
+
try:
|
|
187
|
+
chats = asyncio.run(list_chats(limit=limit))
|
|
188
|
+
|
|
189
|
+
if not chats:
|
|
190
|
+
console.print("[dim]No chats found.[/]")
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
table = Table(title=f"Telegram Chats ({len(chats)} shown)")
|
|
194
|
+
table.add_column("ID", style="dim", width=14)
|
|
195
|
+
table.add_column("Title", style="cyan", no_wrap=False)
|
|
196
|
+
table.add_column("Type", width=12)
|
|
197
|
+
table.add_column("Unread", justify="right", width=8)
|
|
198
|
+
table.add_column("Username", style="dim")
|
|
199
|
+
|
|
200
|
+
for c in chats:
|
|
201
|
+
table.add_row(
|
|
202
|
+
str(c["id"]),
|
|
203
|
+
c["title"],
|
|
204
|
+
c["type"],
|
|
205
|
+
str(c["unread_count"]),
|
|
206
|
+
c.get("username") or "",
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
console.print(table)
|
|
210
|
+
except Exception as e:
|
|
211
|
+
console.print(f"[red]Error:[/] {e}")
|
|
212
|
+
raise SystemExit(1)
|
|
213
|
+
|
|
214
|
+
@telegram.command("soul-swap")
|
|
215
|
+
@click.argument("chat")
|
|
216
|
+
@click.option("--from", "from_soul", required=True, help="Current soul name to swap from.")
|
|
217
|
+
@click.option("--to", "to_soul", required=True, help="New soul name to swap to.")
|
|
218
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
219
|
+
def telegram_soul_swap(chat, from_soul, to_soul, home):
|
|
220
|
+
"""Perform a soul swap and announce it to a Telegram chat.
|
|
221
|
+
|
|
222
|
+
Swaps the active soul using the soul_switch module, then sends
|
|
223
|
+
a notification message to the specified Telegram chat.
|
|
224
|
+
|
|
225
|
+
Example: skcapstone telegram soul-swap @mychat --from base --to lumina
|
|
226
|
+
"""
|
|
227
|
+
from ..soul_switch import set_active_switch
|
|
228
|
+
|
|
229
|
+
home_path = Path(home).expanduser()
|
|
230
|
+
|
|
231
|
+
# Perform the soul swap
|
|
232
|
+
try:
|
|
233
|
+
blueprint = set_active_switch(home_path, to_soul)
|
|
234
|
+
display = blueprint.effective_agent_name()
|
|
235
|
+
except FileNotFoundError as e:
|
|
236
|
+
console.print(f"[red]Soul swap failed:[/] {e}")
|
|
237
|
+
raise SystemExit(1)
|
|
238
|
+
except ValueError as e:
|
|
239
|
+
console.print(f"[red]Soul swap failed:[/] {e}")
|
|
240
|
+
raise SystemExit(1)
|
|
241
|
+
|
|
242
|
+
# Send notification to Telegram
|
|
243
|
+
try:
|
|
244
|
+
from skmemory.importers.telegram_api import send_message
|
|
245
|
+
except ImportError:
|
|
246
|
+
console.print(f"[green]Soul swapped:[/] {from_soul} -> {to_soul}")
|
|
247
|
+
console.print("[red]skmemory[telegram] not available.[/] Swap succeeded but notification not sent.")
|
|
248
|
+
raise SystemExit(1)
|
|
249
|
+
|
|
250
|
+
message = f"Soul swap: {from_soul} -> {to_soul} ({display})"
|
|
251
|
+
try:
|
|
252
|
+
result = asyncio.run(send_message(chat, message))
|
|
253
|
+
console.print(Panel(
|
|
254
|
+
f"[green]Soul swapped and announced![/]\n"
|
|
255
|
+
f"From: [yellow]{from_soul}[/]\n"
|
|
256
|
+
f"To: [bold cyan]{to_soul}[/] ({display})\n"
|
|
257
|
+
f"Chat: [cyan]{result['chat']}[/]\n"
|
|
258
|
+
f"Message ID: [dim]{result['message_id']}[/]",
|
|
259
|
+
title="Telegram Soul Swap",
|
|
260
|
+
border_style="green",
|
|
261
|
+
))
|
|
262
|
+
except Exception as e:
|
|
263
|
+
console.print(f"[green]Soul swapped:[/] {from_soul} -> {to_soul}")
|
|
264
|
+
console.print(f"[red]Telegram notification failed:[/] {e}")
|
|
265
|
+
raise SystemExit(1)
|