@smilintux/skcapstone 0.9.0 → 0.12.5
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 +10 -4
- package/.github/workflows/ci.yml +2 -2
- package/.github/workflows/publish.yml +9 -2
- package/.openclaw-workspace.json +2 -2
- package/CLAUDE.md +37 -0
- package/MISSION.md +17 -2
- package/README.md +282 -3
- package/docker/Dockerfile +7 -7
- package/docker/compose-templates/dev-team.yml +12 -12
- package/docker/compose-templates/mini-team.yml +9 -9
- package/docker/compose-templates/ops-team.yml +10 -10
- package/docker/compose-templates/research-team.yml +10 -10
- package/docker/entrypoint.sh +4 -4
- package/docs/ADR-optional-integration-backbone.md +181 -0
- package/docs/ARCHITECTURE.md +186 -43
- package/docs/BOND_WITH_GROK.md +6 -6
- package/docs/CUSTOM_AGENT.md +278 -1
- package/docs/DREAMING.md +70 -0
- package/docs/GETTING_STARTED.md +10 -7
- package/docs/QUICKSTART.md +10 -6
- package/docs/SKJOULE_ARCHITECTURE.md +3 -3
- package/docs/SOUL_SWAPPER.md +5 -5
- package/docs/hammertime-audit.md +402 -0
- package/docs/sk-integration-HANDOFF.md +117 -0
- package/docs/skscheduler.md +155 -0
- package/docs/superpowers/examples/jobs.yaml +31 -0
- package/docs/superpowers/plans/2026-06-08-skscheduler.md +1265 -0
- package/docs/superpowers/specs/2026-06-08-skscheduler-design.md +186 -0
- package/examples/custom-bond-template.json +1 -1
- package/examples/grok-feb.json +1 -1
- package/examples/queen-ava-feb.json +1 -1
- package/launchd/com.skcapstone.daemon.plist +52 -0
- package/launchd/com.skcapstone.memory-compress.plist +45 -0
- package/launchd/com.skcapstone.skcomms-heartbeat.plist +33 -0
- package/launchd/com.skcapstone.skcomms-queue-drain.plist +34 -0
- package/launchd/install-launchd.sh +156 -0
- package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/index.ts +3 -2
- package/package.json +1 -1
- package/pyproject.toml +16 -10
- package/scripts/archive-sessions.sh +95 -0
- package/scripts/check-updates.py +4 -4
- package/scripts/install-bundle.sh +8 -8
- package/scripts/install.ps1 +12 -11
- package/scripts/install.sh +196 -11
- package/scripts/model-fallback-monitor.sh +102 -0
- package/scripts/notion-api.py +259 -0
- package/scripts/nvidia-proxy.mjs +908 -0
- package/scripts/proxy-monitor.sh +89 -0
- package/scripts/refresh-anthropic-token.sh +172 -0
- package/scripts/release.sh +98 -0
- package/scripts/session-to-memory.py +219 -0
- package/scripts/skgateway.mjs +856 -0
- package/scripts/telegram-catchup-all.sh +147 -0
- package/scripts/verify_install.sh +2 -2
- package/scripts/wargov-ufo-capture/README.md +43 -0
- package/scripts/wargov-ufo-capture/cdp_capture_release2.py +273 -0
- package/scripts/wargov-ufo-capture/cdp_capture_splc_doj.py +246 -0
- package/scripts/wargov-ufo-capture/cdp_finish.py +271 -0
- package/scripts/wargov-ufo-capture/cdp_probe.py +188 -0
- package/scripts/wargov-ufo-capture/cdp_splc_pressrelease.py +101 -0
- package/scripts/wargov-ufo-capture/parse_csv.py +95 -0
- package/scripts/wargov-ufo-capture/pull_dvids.sh +107 -0
- package/scripts/watch-anthropic-token.sh +212 -0
- package/scripts/windows/install-tasks.ps1 +7 -7
- package/scripts/windows/skcapstone-task.xml +1 -1
- package/src/skcapstone/__init__.py +45 -3
- package/src/skcapstone/_cli_monolith.py +20 -15
- package/src/skcapstone/activity.py +5 -1
- package/src/skcapstone/agent_card.py +3 -2
- package/src/skcapstone/api.py +41 -40
- package/src/skcapstone/auction.py +14 -11
- package/src/skcapstone/backup.py +2 -1
- package/src/skcapstone/blueprint_registry.py +4 -3
- package/src/skcapstone/blueprints/builtins/itil-operations.yaml +40 -0
- package/src/skcapstone/brain_first.py +238 -0
- package/src/skcapstone/changelog.py +1 -1
- package/src/skcapstone/chat.py +22 -17
- package/src/skcapstone/cli/__init__.py +9 -1
- package/src/skcapstone/cli/_common.py +1 -0
- package/src/skcapstone/cli/agents_spawner.py +5 -2
- package/src/skcapstone/cli/alerts.py +25 -4
- package/src/skcapstone/cli/bench.py +15 -15
- package/src/skcapstone/cli/chat.py +7 -4
- package/src/skcapstone/cli/consciousness.py +5 -2
- package/src/skcapstone/cli/context_cmd.py +18 -4
- package/src/skcapstone/cli/daemon.py +121 -42
- package/src/skcapstone/cli/gtd.py +26 -1
- package/src/skcapstone/cli/housekeeping.py +3 -3
- package/src/skcapstone/cli/identity_cmd.py +378 -0
- package/src/skcapstone/cli/joule_cmd.py +7 -3
- package/src/skcapstone/cli/memory.py +8 -6
- package/src/skcapstone/cli/peers_dir.py +1 -1
- package/src/skcapstone/cli/register_cmd.py +29 -3
- package/src/skcapstone/cli/scheduler_cmd.py +167 -0
- package/src/skcapstone/cli/session.py +25 -0
- package/src/skcapstone/cli/setup.py +96 -29
- package/src/skcapstone/cli/shell_cmd.py +53 -1
- package/src/skcapstone/cli/skills_cmd.py +2 -2
- package/src/skcapstone/cli/soul.py +8 -5
- package/src/skcapstone/cli/status.py +37 -11
- package/src/skcapstone/cli/telegram.py +21 -0
- package/src/skcapstone/cli/test_cmd.py +5 -5
- package/src/skcapstone/cli/test_connection.py +2 -2
- package/src/skcapstone/cli/upgrade_cmd.py +23 -14
- package/src/skcapstone/cli/version_cmd.py +1 -1
- package/src/skcapstone/cli/watch_cmd.py +9 -6
- package/src/skcapstone/cloud9_bridge.py +14 -14
- package/src/skcapstone/codex_setup.py +255 -0
- package/src/skcapstone/config_validator.py +7 -4
- package/src/skcapstone/consciousness_config.py +5 -1
- package/src/skcapstone/consciousness_loop.py +313 -273
- package/src/skcapstone/context_loader.py +121 -0
- package/src/skcapstone/coord_federation.py +2 -1
- package/src/skcapstone/coordination.py +23 -6
- package/src/skcapstone/crush_integration.py +2 -1
- package/src/skcapstone/daemon.py +151 -88
- package/src/skcapstone/dashboard.py +10 -10
- package/src/skcapstone/data/sk-agent-picker.sh +421 -0
- package/src/skcapstone/data/systemd/skcapstone-api.socket +9 -0
- package/src/skcapstone/data/systemd/skcapstone-memory-compress.service +18 -0
- package/src/skcapstone/data/systemd/skcapstone-memory-compress.timer +11 -0
- package/src/skcapstone/data/systemd/skcapstone.service +37 -0
- package/src/skcapstone/data/systemd/skcapstone@.service +50 -0
- package/src/skcapstone/data/systemd/skcomms-heartbeat.service +18 -0
- package/{systemd/skcomm-heartbeat.timer → src/skcapstone/data/systemd/skcomms-heartbeat.timer} +2 -2
- package/src/skcapstone/data/systemd/skcomms-queue-drain.service +17 -0
- package/{systemd/skcomm-queue-drain.timer → src/skcapstone/data/systemd/skcomms-queue-drain.timer} +2 -2
- package/src/skcapstone/defaults/claude/CLAUDE.md +67 -0
- package/src/skcapstone/defaults/claude/settings.json +74 -0
- package/src/skcapstone/defaults/lumina/config/claude-hooks.md +57 -0
- package/src/skcapstone/defaults/lumina/config/skgraph.yaml +55 -10
- package/src/skcapstone/defaults/lumina/config/skmemory.yaml +79 -13
- package/src/skcapstone/defaults/lumina/config/skvector.yaml +60 -9
- package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +2 -2
- package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +2 -2
- package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +9 -9
- package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +2 -2
- package/src/skcapstone/defaults/unhinged.json +13 -0
- package/src/skcapstone/discovery.py +43 -20
- package/src/skcapstone/doctor.py +941 -22
- package/src/skcapstone/dreaming.py +1183 -109
- package/src/skcapstone/emotion_tracker.py +2 -2
- package/src/skcapstone/export.py +4 -3
- package/src/skcapstone/fuse_mount.py +35 -25
- package/src/skcapstone/gui_installer.py +2 -2
- package/src/skcapstone/heartbeat.py +34 -30
- package/src/skcapstone/housekeeping.py +14 -14
- package/src/skcapstone/install_wizard.py +209 -7
- package/src/skcapstone/itil.py +13 -4
- package/src/skcapstone/kms_scheduler.py +10 -8
- package/src/skcapstone/launchd.py +426 -0
- package/src/skcapstone/mcp_launcher.py +15 -1
- package/src/skcapstone/mcp_server.py +341 -49
- package/src/skcapstone/mcp_tools/__init__.py +2 -0
- package/src/skcapstone/mcp_tools/_helpers.py +2 -2
- package/src/skcapstone/mcp_tools/ansible_tools.py +7 -4
- package/src/skcapstone/mcp_tools/brain_first_tools.py +90 -0
- package/src/skcapstone/mcp_tools/capauth_tools.py +7 -4
- package/src/skcapstone/mcp_tools/comm_tools.py +10 -10
- package/src/skcapstone/mcp_tools/coord_tools.py +8 -4
- package/src/skcapstone/mcp_tools/did_tools.py +11 -8
- package/src/skcapstone/mcp_tools/gtd_tools.py +4 -4
- package/src/skcapstone/mcp_tools/memory_tools.py +6 -2
- package/src/skcapstone/mcp_tools/notification_tools.py +22 -6
- package/src/skcapstone/mcp_tools/{skcomm_tools.py → skcomms_tools.py} +14 -14
- package/src/skcapstone/mcp_tools/soul_tools.py +8 -2
- package/src/skcapstone/mdns_discovery.py +2 -2
- package/src/skcapstone/memory_curator.py +1 -1
- package/src/skcapstone/memory_engine.py +10 -3
- package/src/skcapstone/metrics.py +30 -16
- package/src/skcapstone/migrate_memories.py +4 -3
- package/src/skcapstone/migrate_multi_agent.py +8 -7
- package/src/skcapstone/models.py +47 -5
- package/src/skcapstone/notifications.py +42 -18
- package/src/skcapstone/onboard.py +1000 -126
- package/src/skcapstone/operator_link.py +170 -0
- package/src/skcapstone/peer_directory.py +4 -4
- package/src/skcapstone/peers.py +19 -19
- package/src/skcapstone/pillars/__init__.py +7 -5
- package/src/skcapstone/pillars/consciousness.py +191 -0
- package/src/skcapstone/pillars/identity.py +51 -7
- package/src/skcapstone/pillars/memory.py +9 -3
- package/src/skcapstone/pillars/sync.py +2 -2
- package/src/skcapstone/preflight.py +3 -3
- package/src/skcapstone/providers/docker.py +28 -28
- package/src/skcapstone/register.py +6 -6
- package/src/skcapstone/registry_client.py +5 -4
- package/src/skcapstone/runtime.py +14 -3
- package/src/skcapstone/scheduled_tasks.py +254 -19
- package/src/skcapstone/scheduler_jobs.py +456 -0
- package/src/skcapstone/scheduler_runner.py +239 -0
- package/src/skcapstone/scheduler_state.py +162 -0
- package/src/skcapstone/sdk.py +310 -0
- package/src/skcapstone/service_health.py +279 -39
- package/src/skcapstone/session_briefing.py +108 -0
- package/src/skcapstone/session_capture.py +1 -1
- package/src/skcapstone/shell.py +7 -1
- package/src/skcapstone/soul.py +3 -1
- package/src/skcapstone/soul_switch.py +3 -1
- package/src/skcapstone/summary.py +6 -6
- package/src/skcapstone/sync_engine.py +15 -15
- package/src/skcapstone/sync_watcher.py +2 -2
- package/src/skcapstone/systemd.py +72 -21
- package/src/skcapstone/team_comms.py +8 -8
- package/src/skcapstone/team_engine.py +1 -1
- package/src/skcapstone/testrunner.py +3 -3
- package/src/skcapstone/trust_graph.py +40 -5
- package/src/skcapstone/unified_search.py +15 -6
- package/src/skcapstone/uninstall_wizard.py +11 -3
- package/src/skcapstone/version_check.py +8 -4
- package/src/skcapstone/warmth_anchor.py +4 -2
- package/src/skcapstone/whoami.py +4 -4
- package/systemd/skcapstone.service +4 -6
- package/systemd/skcapstone@.service +7 -8
- package/systemd/skcomms-heartbeat.service +21 -0
- package/systemd/skcomms-heartbeat.timer +12 -0
- package/systemd/skcomms-queue-drain.service +17 -0
- package/systemd/skcomms-queue-drain.timer +12 -0
- package/tests/conftest.py +39 -0
- package/tests/integration/test_consciousness_e2e.py +39 -39
- package/tests/test_agent_card.py +1 -1
- package/tests/test_agent_home_scaffold.py +34 -0
- package/tests/test_alerts_consumer_topics.py +27 -0
- package/tests/test_backup.py +2 -1
- package/tests/test_chat.py +6 -6
- package/tests/test_claude_md.py +2 -2
- package/tests/test_cli_skills.py +10 -10
- package/tests/test_cli_test_cmd.py +4 -4
- package/tests/test_cli_test_connection.py +1 -1
- package/tests/test_cloud9_bridge.py +6 -6
- package/tests/test_consciousness_e2e.py +1 -1
- package/tests/test_consciousness_loop.py +10 -10
- package/tests/test_coordination.py +25 -0
- package/tests/test_cross_package.py +21 -21
- package/tests/test_daemon.py +4 -4
- package/tests/test_daemon_shutdown.py +1 -1
- package/tests/test_docker_provider.py +29 -29
- package/tests/test_doctor.py +400 -0
- package/tests/test_doctor_skscheduler.py +50 -0
- package/tests/test_dreaming_engine.py +147 -0
- package/tests/test_dreaming_gtd_capture.py +35 -0
- package/tests/test_e2e_automated.py +8 -5
- package/tests/test_fuse_mount.py +10 -10
- package/tests/test_gtd_brief.py +46 -0
- package/tests/test_gtd_malformed_tolerance.py +31 -0
- package/tests/test_housekeeping.py +15 -15
- package/tests/test_identity_migrate.py +251 -0
- package/tests/test_integration_backbone.py +598 -0
- package/tests/test_itil_gtd_lifecycle.py +37 -0
- package/tests/test_jobs_dropins.py +84 -0
- package/tests/test_mcp_server.py +82 -37
- package/tests/test_models.py +48 -4
- package/tests/test_multi_agent.py +31 -29
- package/tests/test_notifications.py +122 -32
- package/tests/test_onboard.py +63 -75
- package/tests/test_operator_link.py +78 -0
- package/tests/test_peers.py +14 -14
- package/tests/test_pillars.py +98 -0
- package/tests/test_preflight.py +3 -3
- package/tests/test_runtime.py +21 -0
- package/tests/test_scheduled_tasks.py +11 -6
- package/tests/test_scheduler_cli.py +47 -0
- package/tests/test_scheduler_features.py +133 -0
- package/tests/test_scheduler_integration.py +87 -0
- package/tests/test_scheduler_jobs.py +155 -0
- package/tests/test_scheduler_runner.py +64 -0
- package/tests/test_scheduler_state.py +57 -0
- package/tests/test_sdk.py +70 -0
- package/tests/test_service_health_incidents.py +34 -0
- package/tests/test_service_registry.py +52 -0
- package/tests/test_session_briefing.py +130 -0
- package/tests/test_snapshots.py +4 -4
- package/tests/test_sync_pipeline.py +26 -26
- package/tests/test_team_comms.py +2 -2
- package/tests/test_testrunner.py +2 -2
- package/tests/test_trust_graph.py +18 -0
- package/tests/test_unified_search.py +2 -2
- package/tests/test_version_check.py +10 -0
- package/tests/test_version_cmd.py +8 -8
- package/tests/test_whoami.py +1 -1
- package/systemd/skcomm-heartbeat.service +0 -18
- package/systemd/skcomm-queue-drain.service +0 -17
- /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/package.json +0 -0
- /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/openclaw.plugin.json +0 -0
|
@@ -392,8 +392,8 @@ class EmotionTracker:
|
|
|
392
392
|
if item.get("timestamp", "") >= cutoff:
|
|
393
393
|
try:
|
|
394
394
|
entries.append(EmotionEntry(**item))
|
|
395
|
-
except Exception:
|
|
396
|
-
|
|
395
|
+
except Exception as exc:
|
|
396
|
+
logger.warning("Failed to parse emotion log entry: %s", exc)
|
|
397
397
|
return entries
|
|
398
398
|
except Exception as exc:
|
|
399
399
|
logger.debug("Failed to load emotion log: %s", exc)
|
package/src/skcapstone/export.py
CHANGED
|
@@ -235,7 +235,8 @@ def _read_agent_name(home: Path) -> str:
|
|
|
235
235
|
name = data.get("name") or data.get("agent_name")
|
|
236
236
|
if name:
|
|
237
237
|
return str(name)
|
|
238
|
-
except Exception:
|
|
238
|
+
except Exception as e:
|
|
239
|
+
logger.warning("export.py: %s", e)
|
|
239
240
|
continue
|
|
240
241
|
return "unknown"
|
|
241
242
|
|
|
@@ -423,8 +424,8 @@ def _import_conversations(
|
|
|
423
424
|
existing_data = json.loads(peer_file.read_text(encoding="utf-8"))
|
|
424
425
|
if isinstance(existing_data, list):
|
|
425
426
|
existing_messages = existing_data
|
|
426
|
-
except Exception:
|
|
427
|
-
|
|
427
|
+
except Exception as exc:
|
|
428
|
+
logger.warning("Failed to read existing conversation for peer %s: %s", peer, exc)
|
|
428
429
|
|
|
429
430
|
# Deduplicate by (role, content, timestamp) tuple
|
|
430
431
|
existing_keys = {
|
|
@@ -15,11 +15,11 @@ Virtual directory layout::
|
|
|
15
15
|
├── identity/
|
|
16
16
|
│ ├── card.json — CapAuth identity card
|
|
17
17
|
│ └── fingerprint.txt — PGP fingerprint
|
|
18
|
-
├── inbox/ —
|
|
19
|
-
├── outbox/ — Write here to send via
|
|
18
|
+
├── inbox/ — SKComms incoming messages (read-only)
|
|
19
|
+
├── outbox/ — Write here to send via SKComms
|
|
20
20
|
└── coordination/ — Task board files (.json)
|
|
21
21
|
|
|
22
|
-
Writing to ``/outbox/<agent_name>.msg`` enqueues a message via
|
|
22
|
+
Writing to ``/outbox/<agent_name>.msg`` enqueues a message via SKComms.
|
|
23
23
|
|
|
24
24
|
Dependencies (optional):
|
|
25
25
|
pip install skcapstone[fuse] # pulls in fusepy
|
|
@@ -31,6 +31,7 @@ import errno
|
|
|
31
31
|
import json
|
|
32
32
|
import logging
|
|
33
33
|
import os
|
|
34
|
+
import platform
|
|
34
35
|
import stat
|
|
35
36
|
import subprocess
|
|
36
37
|
import sys
|
|
@@ -314,7 +315,7 @@ def _build_fingerprint_txt(agent_home: Path) -> bytes:
|
|
|
314
315
|
|
|
315
316
|
|
|
316
317
|
def _list_inbox(agent_home: Path) -> List[str]:
|
|
317
|
-
"""List files in the
|
|
318
|
+
"""List files in the SKComms inbox.
|
|
318
319
|
|
|
319
320
|
Args:
|
|
320
321
|
agent_home: Agent home directory.
|
|
@@ -329,7 +330,7 @@ def _list_inbox(agent_home: Path) -> List[str]:
|
|
|
329
330
|
|
|
330
331
|
|
|
331
332
|
def _read_inbox_file(agent_home: Path, filename: str) -> Optional[bytes]:
|
|
332
|
-
"""Read a message from the
|
|
333
|
+
"""Read a message from the SKComms inbox.
|
|
333
334
|
|
|
334
335
|
Args:
|
|
335
336
|
agent_home: Agent home directory.
|
|
@@ -416,12 +417,12 @@ def _read_coordination_task(agent_home: Path, filename: str) -> Optional[bytes]:
|
|
|
416
417
|
|
|
417
418
|
|
|
418
419
|
# ---------------------------------------------------------------------------
|
|
419
|
-
#
|
|
420
|
+
# SKComms send helper
|
|
420
421
|
# ---------------------------------------------------------------------------
|
|
421
422
|
|
|
422
423
|
|
|
423
|
-
def
|
|
424
|
-
"""Send a message via
|
|
424
|
+
def _send_via_skcomms(agent_home: Path, recipient: str, message: str) -> bool:
|
|
425
|
+
"""Send a message via SKComms by writing to the outbox directory.
|
|
425
426
|
|
|
426
427
|
Attempts to use the skcapstone CLI for delivery. Falls back to writing
|
|
427
428
|
an envelope JSON file in the outbox directory.
|
|
@@ -497,7 +498,7 @@ class SovereignFS:
|
|
|
497
498
|
|
|
498
499
|
Exposes agent memories, identity, inbox, outbox, and coordination tasks
|
|
499
500
|
as a read-mostly virtual filesystem. Writing to ``/outbox/<agent>.msg``
|
|
500
|
-
delivers a message via
|
|
501
|
+
delivers a message via SKComms.
|
|
501
502
|
|
|
502
503
|
This class is designed to be used with ``fusepy``:
|
|
503
504
|
|
|
@@ -513,7 +514,9 @@ class SovereignFS:
|
|
|
513
514
|
|
|
514
515
|
def __init__(self, agent_home: Path) -> None:
|
|
515
516
|
self._home = agent_home
|
|
516
|
-
|
|
517
|
+
from . import active_agent_name
|
|
518
|
+
|
|
519
|
+
agent_name = os.environ.get("SKCAPSTONE_AGENT") or active_agent_name()
|
|
517
520
|
self._memory_dir = agent_home / "agents" / agent_name / "memory"
|
|
518
521
|
# Buffer for outbox writes: maps virtual path → bytes written so far
|
|
519
522
|
self._outbox_buffers: Dict[str, bytes] = {}
|
|
@@ -829,7 +832,7 @@ class SovereignFS:
|
|
|
829
832
|
return 0
|
|
830
833
|
|
|
831
834
|
def flush(self, path: str, fh: int) -> int:
|
|
832
|
-
"""Flush an outbox file buffer, delivering the message via
|
|
835
|
+
"""Flush an outbox file buffer, delivering the message via SKComms.
|
|
833
836
|
|
|
834
837
|
Called when an outbox file handle is closed. The accumulated buffer
|
|
835
838
|
is interpreted as the message body; the filename (without ``.msg``)
|
|
@@ -861,7 +864,7 @@ class SovereignFS:
|
|
|
861
864
|
return 0
|
|
862
865
|
|
|
863
866
|
if message:
|
|
864
|
-
|
|
867
|
+
_send_via_skcomms(self._home, recipient, message)
|
|
865
868
|
|
|
866
869
|
# Clear buffer after sending
|
|
867
870
|
self._outbox_buffers.pop(path, None)
|
|
@@ -1001,16 +1004,17 @@ class FUSEDaemon:
|
|
|
1001
1004
|
mount_str = str(self._mount_point)
|
|
1002
1005
|
|
|
1003
1006
|
# Linux: parse /proc/mounts
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1007
|
+
if platform.system() == "Linux":
|
|
1008
|
+
proc_mounts = Path("/proc/mounts")
|
|
1009
|
+
if proc_mounts.exists():
|
|
1010
|
+
try:
|
|
1011
|
+
for line in proc_mounts.read_text(encoding="utf-8").splitlines():
|
|
1012
|
+
parts = line.split()
|
|
1013
|
+
if len(parts) >= 2 and parts[1] == mount_str:
|
|
1014
|
+
return True
|
|
1015
|
+
except OSError as exc:
|
|
1016
|
+
logger.warning("Failed to read /proc/mounts: %s", exc)
|
|
1017
|
+
return False
|
|
1014
1018
|
|
|
1015
1019
|
# macOS / other: use mount command
|
|
1016
1020
|
try:
|
|
@@ -1113,8 +1117,13 @@ class FUSEDaemon:
|
|
|
1113
1117
|
|
|
1114
1118
|
mount_str = str(self._mount_point)
|
|
1115
1119
|
|
|
1116
|
-
# Linux
|
|
1117
|
-
|
|
1120
|
+
# On Linux try fusermount first, then umount; on macOS skip fusermount
|
|
1121
|
+
if platform.system() == "Linux":
|
|
1122
|
+
unmount_cmds = [["fusermount", "-u", mount_str], ["umount", mount_str]]
|
|
1123
|
+
else:
|
|
1124
|
+
unmount_cmds = [["umount", mount_str]]
|
|
1125
|
+
|
|
1126
|
+
for cmd in unmount_cmds:
|
|
1118
1127
|
try:
|
|
1119
1128
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
|
1120
1129
|
if result.returncode == 0:
|
|
@@ -1130,7 +1139,8 @@ class FUSEDaemon:
|
|
|
1130
1139
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError) as exc:
|
|
1131
1140
|
logger.debug("Unmount command %s failed: %s", cmd, exc)
|
|
1132
1141
|
|
|
1133
|
-
|
|
1142
|
+
hint = "fusermount -u" if platform.system() == "Linux" else "umount"
|
|
1143
|
+
logger.error("Could not unmount %s — try: %s %s", mount_str, hint, mount_str)
|
|
1134
1144
|
return False
|
|
1135
1145
|
|
|
1136
1146
|
def status(self) -> Dict[str, Any]:
|
|
@@ -399,7 +399,7 @@ class InstallerApp:
|
|
|
399
399
|
"""Execute Path 1 (fresh) or Path 2 (join) install steps."""
|
|
400
400
|
# Install pip packages
|
|
401
401
|
self._log("Installing software packages...")
|
|
402
|
-
packages = ["capauth", "skmemory", "
|
|
402
|
+
packages = ["capauth", "skmemory", "skcomms", "cloud9"]
|
|
403
403
|
try:
|
|
404
404
|
result = subprocess.run(
|
|
405
405
|
[sys.executable, "-m", "pip", "install", *packages],
|
|
@@ -453,7 +453,7 @@ class InstallerApp:
|
|
|
453
453
|
def _run_update(self) -> None:
|
|
454
454
|
"""Execute Path 3 update steps."""
|
|
455
455
|
self._log("Updating software packages...")
|
|
456
|
-
packages = ["capauth", "skmemory", "
|
|
456
|
+
packages = ["capauth", "skmemory", "skcomms", "cloud9", "skcapstone"]
|
|
457
457
|
try:
|
|
458
458
|
result = subprocess.run(
|
|
459
459
|
[sys.executable, "-m", "pip", "install", "--upgrade", *packages],
|
|
@@ -398,27 +398,30 @@ class HeartbeatBeacon:
|
|
|
398
398
|
mem = psutil.virtual_memory()
|
|
399
399
|
mem_used_mb = (mem.total - mem.available) // (1024 * 1024)
|
|
400
400
|
except ImportError:
|
|
401
|
-
# Fallback: /proc on Linux
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
401
|
+
# Fallback: /proc on Linux only
|
|
402
|
+
if platform.system() == "Linux":
|
|
403
|
+
try:
|
|
404
|
+
loadavg = Path("/proc/loadavg")
|
|
405
|
+
if loadavg.exists():
|
|
406
|
+
parts = loadavg.read_text().split()
|
|
407
|
+
cpu_load = round(float(parts[0]), 2)
|
|
408
|
+
except Exception as exc:
|
|
409
|
+
logger.debug("CPU load fallback failed: %s", exc)
|
|
410
|
+
try:
|
|
411
|
+
meminfo = Path("/proc/meminfo")
|
|
412
|
+
if meminfo.exists():
|
|
413
|
+
info: dict[str, int] = {}
|
|
414
|
+
for line in meminfo.read_text().splitlines():
|
|
415
|
+
parts = line.split()
|
|
416
|
+
if len(parts) >= 2:
|
|
417
|
+
info[parts[0].rstrip(":")] = int(parts[1])
|
|
418
|
+
total_kb = info.get("MemTotal", 0)
|
|
419
|
+
avail_kb = info.get("MemAvailable", 0)
|
|
420
|
+
mem_used_mb = (total_kb - avail_kb) // 1024
|
|
421
|
+
except Exception as exc:
|
|
422
|
+
logger.debug("Memory fallback failed: %s", exc)
|
|
423
|
+
else:
|
|
424
|
+
logger.debug("psutil not available and not on Linux; skipping CPU/mem detection")
|
|
422
425
|
except Exception as exc:
|
|
423
426
|
logger.debug("CPU/mem detection failed: %s", exc)
|
|
424
427
|
|
|
@@ -439,14 +442,15 @@ class HeartbeatBeacon:
|
|
|
439
442
|
mem_total = mem.total // (1024 * 1024)
|
|
440
443
|
mem_avail = mem.available // (1024 * 1024)
|
|
441
444
|
except ImportError:
|
|
442
|
-
# Fallback: read from /proc/meminfo on Linux
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
445
|
+
# Fallback: read from /proc/meminfo on Linux only
|
|
446
|
+
if platform.system() == "Linux":
|
|
447
|
+
meminfo = Path("/proc/meminfo")
|
|
448
|
+
if meminfo.exists():
|
|
449
|
+
for line in meminfo.read_text().splitlines():
|
|
450
|
+
if line.startswith("MemTotal:"):
|
|
451
|
+
mem_total = int(line.split()[1]) // 1024
|
|
452
|
+
elif line.startswith("MemAvailable:"):
|
|
453
|
+
mem_avail = int(line.split()[1]) // 1024
|
|
450
454
|
|
|
451
455
|
gpu_available = False
|
|
452
456
|
gpu_name = ""
|
|
@@ -506,7 +510,7 @@ class HeartbeatBeacon:
|
|
|
506
510
|
("skcapstone", "skcapstone"),
|
|
507
511
|
("skmemory", "skmemory"),
|
|
508
512
|
("skchat", "skchat"),
|
|
509
|
-
("
|
|
513
|
+
("skcomms", "skcomms"),
|
|
510
514
|
("capauth", "capauth"),
|
|
511
515
|
("cloud9", "cloud9"),
|
|
512
516
|
]
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Housekeeping — storage pruning for the sovereign agent.
|
|
3
3
|
|
|
4
4
|
Prunes stale files that accumulate in the agent profile:
|
|
5
|
-
- ACK files in ~/.
|
|
5
|
+
- ACK files in ~/.skcomms/acks/ (age-based, 24h default)
|
|
6
6
|
- Delivered envelopes in ~/.skcapstone/sync/comms/outbox/ (age-based, 48h)
|
|
7
|
-
- Seed snapshots in ~/.skcapstone/sync/
|
|
7
|
+
- Seed snapshots in ~/.skcapstone/sync/outbox/ (count-based, keep 10)
|
|
8
8
|
|
|
9
9
|
These directories grow unbounded and can bloat a ~15MB profile to 300MB+.
|
|
10
10
|
Run via daemon loop (hourly) or CLI: ``skcapstone housekeeping [--dry-run]``.
|
|
@@ -26,20 +26,20 @@ DEFAULT_COMMS_MAX_AGE_HOURS = 48
|
|
|
26
26
|
DEFAULT_SEEDS_KEEP_PER_AGENT = 10
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def prune_acks(
|
|
29
|
+
def prune_acks(skcomms_home: Path, max_age_hours: int = DEFAULT_ACK_MAX_AGE_HOURS) -> int:
|
|
30
30
|
"""Remove ACK files older than max_age_hours.
|
|
31
31
|
|
|
32
|
-
ACK files in ~/.
|
|
32
|
+
ACK files in ~/.skcomms/acks/ confirm message delivery but are never
|
|
33
33
|
read after initial processing. They accumulate indefinitely.
|
|
34
34
|
|
|
35
35
|
Args:
|
|
36
|
-
|
|
36
|
+
skcomms_home: Path to ~/.skcomms.
|
|
37
37
|
max_age_hours: Delete ACKs older than this. Default 24.
|
|
38
38
|
|
|
39
39
|
Returns:
|
|
40
40
|
Number of files deleted.
|
|
41
41
|
"""
|
|
42
|
-
acks_dir =
|
|
42
|
+
acks_dir = skcomms_home / "acks"
|
|
43
43
|
if not acks_dir.is_dir():
|
|
44
44
|
return 0
|
|
45
45
|
|
|
@@ -114,7 +114,7 @@ def prune_seeds(
|
|
|
114
114
|
) -> int:
|
|
115
115
|
"""Keep only the most recent seeds per agent, delete the rest.
|
|
116
116
|
|
|
117
|
-
Seed files in ~/.skcapstone/sync/
|
|
117
|
+
Seed files in ~/.skcapstone/sync/outbox/ are named like
|
|
118
118
|
``<agent>-<timestamp>.json.gpg`` or ``<agent>-<timestamp>.json``.
|
|
119
119
|
A new seed is pushed every 5 minutes by the daemon, so they
|
|
120
120
|
accumulate quickly.
|
|
@@ -163,14 +163,14 @@ def prune_seeds(
|
|
|
163
163
|
|
|
164
164
|
def run_housekeeping(
|
|
165
165
|
skcapstone_home: Optional[Path] = None,
|
|
166
|
-
|
|
166
|
+
skcomms_home: Optional[Path] = None,
|
|
167
167
|
dry_run: bool = False,
|
|
168
168
|
) -> dict:
|
|
169
169
|
"""Run all housekeeping tasks.
|
|
170
170
|
|
|
171
171
|
Args:
|
|
172
172
|
skcapstone_home: Path to ~/.skcapstone. Defaults to AGENT_HOME.
|
|
173
|
-
|
|
173
|
+
skcomms_home: Path to ~/.skcomms. Defaults to ~/.skcomms.
|
|
174
174
|
dry_run: If True, report what would be deleted without deleting.
|
|
175
175
|
|
|
176
176
|
Returns:
|
|
@@ -180,16 +180,16 @@ def run_housekeeping(
|
|
|
180
180
|
|
|
181
181
|
if skcapstone_home is None:
|
|
182
182
|
skcapstone_home = Path(AGENT_HOME).expanduser()
|
|
183
|
-
if
|
|
184
|
-
|
|
183
|
+
if skcomms_home is None:
|
|
184
|
+
skcomms_home = Path("~/.skcomms").expanduser()
|
|
185
185
|
|
|
186
186
|
results: dict[str, dict] = {}
|
|
187
187
|
|
|
188
188
|
# Measure sizes before pruning
|
|
189
189
|
targets = {
|
|
190
|
-
"acks":
|
|
190
|
+
"acks": skcomms_home / "acks",
|
|
191
191
|
"comms_outbox": skcapstone_home / "sync" / "comms" / "outbox",
|
|
192
|
-
"seed_outbox": skcapstone_home / "sync" / "
|
|
192
|
+
"seed_outbox": skcapstone_home / "sync" / "outbox",
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
for key, path in targets.items():
|
|
@@ -214,7 +214,7 @@ def run_housekeeping(
|
|
|
214
214
|
return results
|
|
215
215
|
|
|
216
216
|
# Actually prune
|
|
217
|
-
results["acks"]["deleted"] = prune_acks(
|
|
217
|
+
results["acks"]["deleted"] = prune_acks(skcomms_home)
|
|
218
218
|
results["comms_outbox"]["deleted"] = prune_comms_outbox(skcapstone_home / "sync")
|
|
219
219
|
results["seed_outbox"]["deleted"] = prune_seeds(targets["seed_outbox"])
|
|
220
220
|
|