@smilintux/skcapstone 0.1.0
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/.cursorrules +33 -0
- package/.github/workflows/ci.yml +23 -0
- package/.github/workflows/publish.yml +52 -0
- package/AGENTS.md +74 -0
- package/CLAUDE.md +56 -0
- package/LICENSE +674 -0
- package/README.md +242 -0
- package/SKILL.md +36 -0
- package/bin/cli.js +18 -0
- package/docs/ARCHITECTURE.md +510 -0
- package/docs/SECURITY_DESIGN.md +315 -0
- package/docs/SOVEREIGN_SINGULARITY.md +371 -0
- package/docs/TOKEN_SYSTEM.md +201 -0
- package/index.d.ts +9 -0
- package/index.js +32 -0
- package/package.json +32 -0
- package/pyproject.toml +84 -0
- package/src/skcapstone/__init__.py +13 -0
- package/src/skcapstone/cli.py +1441 -0
- package/src/skcapstone/connectors/__init__.py +6 -0
- package/src/skcapstone/coordination.py +590 -0
- package/src/skcapstone/discovery.py +275 -0
- package/src/skcapstone/memory_engine.py +457 -0
- package/src/skcapstone/models.py +223 -0
- package/src/skcapstone/pillars/__init__.py +8 -0
- package/src/skcapstone/pillars/identity.py +91 -0
- package/src/skcapstone/pillars/memory.py +61 -0
- package/src/skcapstone/pillars/security.py +83 -0
- package/src/skcapstone/pillars/sync.py +486 -0
- package/src/skcapstone/pillars/trust.py +335 -0
- package/src/skcapstone/runtime.py +190 -0
- package/src/skcapstone/skills/__init__.py +1 -0
- package/src/skcapstone/skills/syncthing_setup.py +297 -0
- package/src/skcapstone/sync/__init__.py +14 -0
- package/src/skcapstone/sync/backends.py +330 -0
- package/src/skcapstone/sync/engine.py +301 -0
- package/src/skcapstone/sync/models.py +97 -0
- package/src/skcapstone/sync/vault.py +284 -0
- package/src/skcapstone/tokens.py +439 -0
- package/tests/__init__.py +0 -0
- package/tests/conftest.py +42 -0
- package/tests/test_coordination.py +299 -0
- package/tests/test_discovery.py +57 -0
- package/tests/test_memory_engine.py +391 -0
- package/tests/test_models.py +63 -0
- package/tests/test_pillars.py +87 -0
- package/tests/test_runtime.py +60 -0
- package/tests/test_sync.py +507 -0
- package/tests/test_syncthing_setup.py +76 -0
- package/tests/test_tokens.py +265 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Component discovery engine.
|
|
3
|
+
|
|
4
|
+
Auto-detects installed SK ecosystem components and their state.
|
|
5
|
+
No hardcoded paths — probes the environment like a sovereign should.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import importlib
|
|
11
|
+
import json
|
|
12
|
+
import shutil
|
|
13
|
+
import subprocess
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
from .models import (
|
|
19
|
+
IdentityState,
|
|
20
|
+
MemoryState,
|
|
21
|
+
PillarStatus,
|
|
22
|
+
SecurityState,
|
|
23
|
+
SyncState,
|
|
24
|
+
TrustState,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _try_import(module_name: str) -> Optional[object]:
|
|
29
|
+
"""Attempt to import a module, return None if unavailable."""
|
|
30
|
+
try:
|
|
31
|
+
return importlib.import_module(module_name)
|
|
32
|
+
except ImportError:
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _count_json_files(directory: Path) -> int:
|
|
37
|
+
"""Count .json files in a directory (non-recursive)."""
|
|
38
|
+
if not directory.is_dir():
|
|
39
|
+
return 0
|
|
40
|
+
return sum(1 for f in directory.iterdir() if f.suffix == ".json")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def discover_identity(home: Path) -> IdentityState:
|
|
44
|
+
"""Probe for CapAuth identity.
|
|
45
|
+
|
|
46
|
+
Checks:
|
|
47
|
+
1. capauth Python package installed
|
|
48
|
+
2. ~/.skcapstone/identity/ has key material
|
|
49
|
+
3. Falls back to checking ~/.gnupg for existing PGP keys
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
home: The agent home directory (~/.skcapstone).
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
IdentityState with current identity information.
|
|
56
|
+
"""
|
|
57
|
+
state = IdentityState()
|
|
58
|
+
identity_dir = home / "identity"
|
|
59
|
+
|
|
60
|
+
capauth = _try_import("capauth")
|
|
61
|
+
if capauth is not None:
|
|
62
|
+
state.status = PillarStatus.DEGRADED
|
|
63
|
+
|
|
64
|
+
manifest_file = identity_dir / "identity.json"
|
|
65
|
+
if manifest_file.exists():
|
|
66
|
+
try:
|
|
67
|
+
data = json.loads(manifest_file.read_text())
|
|
68
|
+
state.fingerprint = data.get("fingerprint")
|
|
69
|
+
state.name = data.get("name")
|
|
70
|
+
state.email = data.get("email")
|
|
71
|
+
if data.get("created_at"):
|
|
72
|
+
state.created_at = datetime.fromisoformat(data["created_at"])
|
|
73
|
+
state.status = PillarStatus.ACTIVE
|
|
74
|
+
except (json.JSONDecodeError, KeyError, ValueError):
|
|
75
|
+
state.status = PillarStatus.ERROR
|
|
76
|
+
|
|
77
|
+
pub_key = identity_dir / "agent.pub"
|
|
78
|
+
if pub_key.exists():
|
|
79
|
+
state.key_path = pub_key
|
|
80
|
+
if state.status == PillarStatus.MISSING:
|
|
81
|
+
state.status = PillarStatus.DEGRADED
|
|
82
|
+
|
|
83
|
+
return state
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def discover_memory(home: Path) -> MemoryState:
|
|
87
|
+
"""Probe for SKMemory state.
|
|
88
|
+
|
|
89
|
+
Checks (in order):
|
|
90
|
+
1. Built-in memory engine at ~/.skcapstone/memory/
|
|
91
|
+
2. External skmemory package at ~/.skmemory/ (legacy fallback)
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
home: The agent home directory (~/.skcapstone).
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
MemoryState with current memory counts.
|
|
98
|
+
"""
|
|
99
|
+
state = MemoryState()
|
|
100
|
+
|
|
101
|
+
memory_dir = home / "memory"
|
|
102
|
+
if memory_dir.is_dir():
|
|
103
|
+
state.short_term = _count_json_files(memory_dir / "short-term")
|
|
104
|
+
state.mid_term = _count_json_files(memory_dir / "mid-term")
|
|
105
|
+
state.long_term = _count_json_files(memory_dir / "long-term")
|
|
106
|
+
state.total_memories = state.short_term + state.mid_term + state.long_term
|
|
107
|
+
state.store_path = memory_dir
|
|
108
|
+
state.status = PillarStatus.ACTIVE
|
|
109
|
+
return state
|
|
110
|
+
|
|
111
|
+
# Reason: legacy fallback for agents using the external skmemory package
|
|
112
|
+
skmemory = _try_import("skmemory")
|
|
113
|
+
if skmemory is None:
|
|
114
|
+
return state
|
|
115
|
+
|
|
116
|
+
memory_home = Path("~/.skmemory").expanduser()
|
|
117
|
+
if not memory_home.exists():
|
|
118
|
+
state.status = PillarStatus.DEGRADED
|
|
119
|
+
return state
|
|
120
|
+
|
|
121
|
+
memories_dir = memory_home / "memories"
|
|
122
|
+
if memories_dir.is_dir():
|
|
123
|
+
state.short_term = _count_json_files(memories_dir / "short-term")
|
|
124
|
+
state.mid_term = _count_json_files(memories_dir / "mid-term")
|
|
125
|
+
state.long_term = _count_json_files(memories_dir / "long-term")
|
|
126
|
+
state.total_memories = state.short_term + state.mid_term + state.long_term
|
|
127
|
+
|
|
128
|
+
state.store_path = memory_home
|
|
129
|
+
state.status = PillarStatus.ACTIVE if state.total_memories > 0 else PillarStatus.DEGRADED
|
|
130
|
+
|
|
131
|
+
return state
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def discover_trust(home: Path) -> TrustState:
|
|
135
|
+
"""Probe for Cloud 9 trust state.
|
|
136
|
+
|
|
137
|
+
Checks:
|
|
138
|
+
1. cloud9 npm package or cloud9-python pip package
|
|
139
|
+
2. ~/.skcapstone/trust/ for FEB files
|
|
140
|
+
3. Existing FEB files in default locations
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
home: The agent home directory (~/.skcapstone).
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
TrustState with current trust information.
|
|
147
|
+
"""
|
|
148
|
+
state = TrustState()
|
|
149
|
+
trust_dir = home / "trust"
|
|
150
|
+
|
|
151
|
+
cloud9_py = _try_import("cloud9")
|
|
152
|
+
has_cloud9_cli = shutil.which("cloud9") is not None
|
|
153
|
+
has_cloud9_package = cloud9_py is not None or has_cloud9_cli
|
|
154
|
+
|
|
155
|
+
# Reason: trust state is now built into skcapstone via FEB rehydration,
|
|
156
|
+
# so trust.json with valid data means ACTIVE regardless of cloud9 package
|
|
157
|
+
manifest = trust_dir / "trust.json"
|
|
158
|
+
if manifest.exists():
|
|
159
|
+
try:
|
|
160
|
+
data = json.loads(manifest.read_text())
|
|
161
|
+
state.depth = data.get("depth", 0.0)
|
|
162
|
+
state.trust_level = data.get("trust_level", 0.0)
|
|
163
|
+
state.love_intensity = data.get("love_intensity", 0.0)
|
|
164
|
+
state.entangled = data.get("entangled", False)
|
|
165
|
+
if data.get("last_rehydration"):
|
|
166
|
+
state.last_rehydration = datetime.fromisoformat(data["last_rehydration"])
|
|
167
|
+
has_trust_data = state.depth > 0 or state.trust_level > 0
|
|
168
|
+
state.status = PillarStatus.ACTIVE if has_trust_data else PillarStatus.DEGRADED
|
|
169
|
+
return state
|
|
170
|
+
except (json.JSONDecodeError, KeyError, ValueError):
|
|
171
|
+
state.status = PillarStatus.ERROR
|
|
172
|
+
|
|
173
|
+
if not has_cloud9_package:
|
|
174
|
+
return state
|
|
175
|
+
|
|
176
|
+
state.status = PillarStatus.DEGRADED
|
|
177
|
+
|
|
178
|
+
feb_dirs = [
|
|
179
|
+
trust_dir / "febs",
|
|
180
|
+
Path("~/.cloud9/febs").expanduser(),
|
|
181
|
+
trust_dir,
|
|
182
|
+
]
|
|
183
|
+
|
|
184
|
+
total_febs = 0
|
|
185
|
+
for feb_dir in feb_dirs:
|
|
186
|
+
if feb_dir.is_dir():
|
|
187
|
+
total_febs += sum(
|
|
188
|
+
1
|
|
189
|
+
for f in feb_dir.iterdir()
|
|
190
|
+
if f.suffix in (".feb", ".json") and "feb" in f.name.lower()
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
state.feb_count = total_febs
|
|
194
|
+
|
|
195
|
+
if total_febs > 0:
|
|
196
|
+
state.status = PillarStatus.ACTIVE
|
|
197
|
+
|
|
198
|
+
return state
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def discover_security(home: Path) -> SecurityState:
|
|
202
|
+
"""Probe for SKSecurity state.
|
|
203
|
+
|
|
204
|
+
Checks (in order):
|
|
205
|
+
1. Built-in audit log at ~/.skcapstone/security/audit.log
|
|
206
|
+
2. External sksecurity package (enhancer)
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
home: The agent home directory (~/.skcapstone).
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
SecurityState with current security information.
|
|
213
|
+
"""
|
|
214
|
+
state = SecurityState()
|
|
215
|
+
security_dir = home / "security"
|
|
216
|
+
|
|
217
|
+
audit_log = security_dir / "audit.log"
|
|
218
|
+
if audit_log.exists():
|
|
219
|
+
try:
|
|
220
|
+
line_count = sum(1 for _ in audit_log.open())
|
|
221
|
+
state.audit_entries = line_count
|
|
222
|
+
state.status = PillarStatus.ACTIVE
|
|
223
|
+
except OSError:
|
|
224
|
+
state.status = PillarStatus.ERROR
|
|
225
|
+
|
|
226
|
+
manifest = security_dir / "security.json"
|
|
227
|
+
if manifest.exists():
|
|
228
|
+
try:
|
|
229
|
+
data = json.loads(manifest.read_text())
|
|
230
|
+
state.threats_detected = data.get("threats_detected", 0)
|
|
231
|
+
if data.get("last_scan"):
|
|
232
|
+
state.last_scan = datetime.fromisoformat(data["last_scan"])
|
|
233
|
+
state.status = PillarStatus.ACTIVE
|
|
234
|
+
except (json.JSONDecodeError, KeyError, ValueError):
|
|
235
|
+
pass
|
|
236
|
+
|
|
237
|
+
sksecurity = _try_import("sksecurity")
|
|
238
|
+
if sksecurity is not None and state.status == PillarStatus.MISSING:
|
|
239
|
+
state.status = PillarStatus.DEGRADED
|
|
240
|
+
|
|
241
|
+
return state
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def discover_sync(home: Path) -> SyncState:
|
|
245
|
+
"""Probe for Sovereign Singularity sync state.
|
|
246
|
+
|
|
247
|
+
Delegates to the sync pillar's discovery function.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
home: The agent home directory (~/.skcapstone).
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
SyncState reflecting what's configured on disk.
|
|
254
|
+
"""
|
|
255
|
+
from .pillars.sync import discover_sync as _discover
|
|
256
|
+
|
|
257
|
+
return _discover(home)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def discover_all(home: Path) -> dict:
|
|
261
|
+
"""Run full discovery across all pillars including sync.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
home: The agent home directory (~/.skcapstone).
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
Dict with identity, memory, trust, security, sync states.
|
|
268
|
+
"""
|
|
269
|
+
return {
|
|
270
|
+
"identity": discover_identity(home),
|
|
271
|
+
"memory": discover_memory(home),
|
|
272
|
+
"trust": discover_trust(home),
|
|
273
|
+
"security": discover_security(home),
|
|
274
|
+
"sync": discover_sync(home),
|
|
275
|
+
}
|