kelmo 0.1.0__py3-none-any.whl
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.
- kelmo/__init__.py +3 -0
- kelmo/alerts.py +82 -0
- kelmo/archive_adapter.py +1348 -0
- kelmo/archive_admin.py +831 -0
- kelmo/archive_worker.py +35 -0
- kelmo/bootstrap.py +236 -0
- kelmo/broker_launcher.py +79 -0
- kelmo/budget.py +101 -0
- kelmo/cli.py +718 -0
- kelmo/client.py +1449 -0
- kelmo/config.py +567 -0
- kelmo/config_edit.py +68 -0
- kelmo/context_binding.py +229 -0
- kelmo/contracts.py +565 -0
- kelmo/conversation.py +44 -0
- kelmo/creation.py +554 -0
- kelmo/crew_executor.py +209 -0
- kelmo/daemon.py +470 -0
- kelmo/doctor.py +469 -0
- kelmo/dreaming.py +455 -0
- kelmo/dreaming_cli.py +154 -0
- kelmo/editor.py +1295 -0
- kelmo/embeddings.py +741 -0
- kelmo/entry.py +34 -0
- kelmo/errors.py +25 -0
- kelmo/executor.py +394 -0
- kelmo/fact_checks.py +173 -0
- kelmo/git_ingress.py +760 -0
- kelmo/graph.py +348 -0
- kelmo/hot.py +258 -0
- kelmo/hub_delivery.py +462 -0
- kelmo/hub_embed.py +271 -0
- kelmo/initialization.py +39 -0
- kelmo/install/__init__.py +0 -0
- kelmo/install/file_changes.py +105 -0
- kelmo/install/hooks.py +35 -0
- kelmo/install/instructions.py +192 -0
- kelmo/install/preview.py +128 -0
- kelmo/install/providers.py +56 -0
- kelmo/install/removal.py +185 -0
- kelmo/install/runtime_backup.py +30 -0
- kelmo/install/sandbox.py +31 -0
- kelmo/install/session_setup.py +110 -0
- kelmo/install/setup.py +677 -0
- kelmo/install/shared.py +95 -0
- kelmo/install/wizard.py +128 -0
- kelmo/integration.py +77 -0
- kelmo/interface.py +509 -0
- kelmo/journal.py +808 -0
- kelmo/knowledge_sync.py +88 -0
- kelmo/linux_runtime.py +121 -0
- kelmo/mcp.py +827 -0
- kelmo/mcp_proxy.py +263 -0
- kelmo/migration_cli.py +150 -0
- kelmo/migration_report.py +65 -0
- kelmo/model_selection.py +110 -0
- kelmo/model_store.py +248 -0
- kelmo/outcomes.py +112 -0
- kelmo/project_preview.py +47 -0
- kelmo/projects.py +498 -0
- kelmo/promotion.py +103 -0
- kelmo/publication.py +558 -0
- kelmo/quota.py +70 -0
- kelmo/resolution.py +395 -0
- kelmo/runtime.py +152 -0
- kelmo/server.py +2058 -0
- kelmo/server_admin.py +204 -0
- kelmo/service.py +1162 -0
- kelmo/session_control.py +116 -0
- kelmo/session_prompts.py +57 -0
- kelmo/session_review.py +227 -0
- kelmo/session_settings.py +60 -0
- kelmo/session_transcripts.py +167 -0
- kelmo/session_worker.py +414 -0
- kelmo/sessions.py +373 -0
- kelmo/snapshot.py +790 -0
- kelmo/ssh_gate.py +40 -0
- kelmo/state.py +297 -0
- kelmo/store/__init__.py +0 -0
- kelmo/store/config.py +52 -0
- kelmo/store/db.py +234 -0
- kelmo/store/doctor.py +162 -0
- kelmo/store/errors.py +49 -0
- kelmo/store/graph/__init__.py +0 -0
- kelmo/store/graph/analysis.py +135 -0
- kelmo/store/graph/build.py +353 -0
- kelmo/store/graph/index.py +17 -0
- kelmo/store/graph/parser.py +228 -0
- kelmo/store/graph/query.py +245 -0
- kelmo/store/graph/stats.py +22 -0
- kelmo/store/memory/__init__.py +0 -0
- kelmo/store/memory/backfill.py +224 -0
- kelmo/store/memory/chunk.py +500 -0
- kelmo/store/memory/delete.py +86 -0
- kelmo/store/memory/embed.py +384 -0
- kelmo/store/memory/get.py +89 -0
- kelmo/store/memory/list.py +131 -0
- kelmo/store/memory/reembed.py +163 -0
- kelmo/store/memory/search.py +587 -0
- kelmo/store/memory/search_log.py +208 -0
- kelmo/store/memory/stats.py +98 -0
- kelmo/store/memory/store.py +127 -0
- kelmo/store/memory/taxonomy.py +24 -0
- kelmo/store/memory/update.py +168 -0
- kelmo/store/schema.sql +258 -0
- kelmo/supersession.py +55 -0
- kelmo/telemetry.py +62 -0
- kelmo/timings.py +143 -0
- kelmo/visibility.py +44 -0
- kelmo/wiki/__init__.py +1 -0
- kelmo/wiki/context.py +125 -0
- kelmo/wiki/directory.py +14 -0
- kelmo/wiki/engine.py +2236 -0
- kelmo/wiki/file_contract.py +1537 -0
- kelmo/wiki/hot_views.py +287 -0
- kelmo/wiki/links.py +139 -0
- kelmo/wiki/pages.py +935 -0
- kelmo/wiki/policy.py +129 -0
- kelmo/wiki/prompts/compact.md +17 -0
- kelmo/wiki/prompts/conflict.md +13 -0
- kelmo/wiki/schemas/wiki-file-contract/3.0/page.schema.json +214 -0
- kelmo/wiki/schemas/wiki-file-contract/3.0/records.schema.json +575 -0
- kelmo/wiki/schemas/wiki-file-contract/3.0/worker-manifest.schema.json +1484 -0
- kelmo/wiki/views.py +173 -0
- kelmo-0.1.0.data/data/share/kelmo/bootstrap.py +236 -0
- kelmo-0.1.0.data/data/share/kelmo/config.py +567 -0
- kelmo-0.1.0.data/data/share/kelmo/entry.py +34 -0
- kelmo-0.1.0.data/data/share/kelmo/errors.py +25 -0
- kelmo-0.1.0.data/data/share/kelmo/gate.py +50 -0
- kelmo-0.1.0.data/data/share/kelmo/hot.py +258 -0
- kelmo-0.1.0.data/data/share/kelmo/interface.py +509 -0
- kelmo-0.1.0.data/data/share/kelmo/linux_runtime.py +121 -0
- kelmo-0.1.0.data/data/share/kelmo/model_store.py +248 -0
- kelmo-0.1.0.data/data/share/kelmo/runtime.py +152 -0
- kelmo-0.1.0.data/data/share/kelmo/session_settings.py +60 -0
- kelmo-0.1.0.data/scripts/kelmo +39 -0
- kelmo-0.1.0.dist-info/METADATA +225 -0
- kelmo-0.1.0.dist-info/RECORD +140 -0
- kelmo-0.1.0.dist-info/WHEEL +4 -0
- kelmo-0.1.0.dist-info/licenses/LICENSE +202 -0
kelmo/__init__.py
ADDED
kelmo/alerts.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Tell the vault owner when the executor's provider login is lost, once a day while it stays lost, and once
|
|
2
|
+
when the provider answers again.
|
|
3
|
+
|
|
4
|
+
A usage limit needs no one: work waits for the reset by itself. A lost login waits for a person, so it is
|
|
5
|
+
the one wait that alerts. The message goes on stdin to the command named in `[alerts]`; that command owns
|
|
6
|
+
the channel and its secrets, so none of them reach Kelmo. A marker in the state directory holds the outage
|
|
7
|
+
start and the last delivered alert, so a restart neither repeats an alert nor drops one; an undelivered
|
|
8
|
+
alert is tried again on the next lost-login answer.
|
|
9
|
+
"""
|
|
10
|
+
import json
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
import time
|
|
14
|
+
|
|
15
|
+
REMINDER_SECONDS = 86400
|
|
16
|
+
MARKER = 'login-outage.json'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _when(epoch):
|
|
20
|
+
return time.strftime('%Y-%m-%d %H:%M UTC', time.gmtime(epoch))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class LoginWatch:
|
|
24
|
+
def __init__(self, marker, argv=None, label='Kelmo', timeout=30):
|
|
25
|
+
self.marker, self.argv, self.label, self.timeout = marker, argv, label, timeout
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def for_service(cls, service):
|
|
29
|
+
settings = service.config.alerts or {}
|
|
30
|
+
return cls(service.config.state / MARKER, settings.get('command'), settings.get('label', 'Kelmo'))
|
|
31
|
+
|
|
32
|
+
def saw(self, wait, now=None):
|
|
33
|
+
"""Read one checked executor answer: a QuotaWait, or None when the provider answered."""
|
|
34
|
+
now = time.time() if now is None else now
|
|
35
|
+
try:
|
|
36
|
+
if wait is not None and wait.reason == 'login':
|
|
37
|
+
self._lost(now)
|
|
38
|
+
elif self.marker.exists():
|
|
39
|
+
self._back(now)
|
|
40
|
+
except Exception as error: # noqa: BLE001 - Alerting must not turn a wait into a failed attempt.
|
|
41
|
+
print(json.dumps({'alert_delivery': 'failed', 'error': type(error).__name__}), file=sys.stderr, flush=True)
|
|
42
|
+
|
|
43
|
+
def _lost(self, now):
|
|
44
|
+
outage = json.loads(self.marker.read_text(encoding='utf-8')) if self.marker.exists() else None
|
|
45
|
+
if outage is None:
|
|
46
|
+
outage = {'since': now, 'alerted_at': None}
|
|
47
|
+
self._save(outage)
|
|
48
|
+
last = outage['alerted_at']
|
|
49
|
+
if last is not None and now - last < REMINDER_SECONDS:
|
|
50
|
+
return
|
|
51
|
+
text = (f'{self.label}: the executor login is lost since {_when(outage["since"])}. Work waits and is '
|
|
52
|
+
'tried again every 15 minutes; log in on the executor account.')
|
|
53
|
+
if last is not None:
|
|
54
|
+
text += f' Day {int((now - outage["since"]) // REMINDER_SECONDS) + 1}.'
|
|
55
|
+
if self.send(text):
|
|
56
|
+
self._save({**outage, 'alerted_at': now})
|
|
57
|
+
|
|
58
|
+
def _back(self, now):
|
|
59
|
+
outage = json.loads(self.marker.read_text(encoding='utf-8'))
|
|
60
|
+
# The marker goes first: one recovery message per outage, even if its delivery fails.
|
|
61
|
+
self.marker.unlink()
|
|
62
|
+
hours = round((now - outage['since']) / 3600, 1)
|
|
63
|
+
self.send(f'{self.label}: the executor login works again after {hours} h; waiting work resumes.')
|
|
64
|
+
|
|
65
|
+
def _save(self, outage):
|
|
66
|
+
partial = self.marker.with_name(self.marker.name + '.partial')
|
|
67
|
+
partial.write_text(json.dumps(outage), encoding='utf-8')
|
|
68
|
+
partial.replace(self.marker)
|
|
69
|
+
|
|
70
|
+
def send(self, text):
|
|
71
|
+
"""Journal line plus the configured command; True once the command accepts the message."""
|
|
72
|
+
print('kelmo worker: alert: ' + text, file=sys.stderr, flush=True)
|
|
73
|
+
if not self.argv:
|
|
74
|
+
return False
|
|
75
|
+
try:
|
|
76
|
+
done = subprocess.run(self.argv, input=text, text=True, capture_output=True, timeout=self.timeout)
|
|
77
|
+
except (OSError, subprocess.TimeoutExpired) as error:
|
|
78
|
+
print(json.dumps({'alert_delivery': 'failed', 'error': type(error).__name__}), file=sys.stderr, flush=True)
|
|
79
|
+
return False
|
|
80
|
+
if done.returncode != 0:
|
|
81
|
+
print(json.dumps({'alert_delivery': 'failed', 'exit': done.returncode}), file=sys.stderr, flush=True)
|
|
82
|
+
return done.returncode == 0
|