abstractgateway 0.1.0__py3-none-any.whl → 0.1.1__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.
- abstractgateway/__init__.py +1 -2
- abstractgateway/__main__.py +7 -0
- abstractgateway/app.py +4 -4
- abstractgateway/cli.py +568 -8
- abstractgateway/config.py +15 -5
- abstractgateway/embeddings_config.py +45 -0
- abstractgateway/host_metrics.py +274 -0
- abstractgateway/hosts/bundle_host.py +528 -55
- abstractgateway/hosts/visualflow_host.py +30 -3
- abstractgateway/integrations/__init__.py +2 -0
- abstractgateway/integrations/email_bridge.py +782 -0
- abstractgateway/integrations/telegram_bridge.py +534 -0
- abstractgateway/maintenance/__init__.py +5 -0
- abstractgateway/maintenance/action_tokens.py +100 -0
- abstractgateway/maintenance/backlog_exec_runner.py +1592 -0
- abstractgateway/maintenance/backlog_parser.py +184 -0
- abstractgateway/maintenance/draft_generator.py +451 -0
- abstractgateway/maintenance/llm_assist.py +212 -0
- abstractgateway/maintenance/notifier.py +109 -0
- abstractgateway/maintenance/process_manager.py +1064 -0
- abstractgateway/maintenance/report_models.py +81 -0
- abstractgateway/maintenance/report_parser.py +219 -0
- abstractgateway/maintenance/text_similarity.py +123 -0
- abstractgateway/maintenance/triage.py +507 -0
- abstractgateway/maintenance/triage_queue.py +142 -0
- abstractgateway/migrate.py +155 -0
- abstractgateway/routes/__init__.py +2 -2
- abstractgateway/routes/gateway.py +10817 -179
- abstractgateway/routes/triage.py +118 -0
- abstractgateway/runner.py +689 -14
- abstractgateway/security/gateway_security.py +425 -110
- abstractgateway/service.py +213 -6
- abstractgateway/stores.py +64 -4
- abstractgateway/workflow_deprecations.py +225 -0
- abstractgateway-0.1.1.dist-info/METADATA +135 -0
- abstractgateway-0.1.1.dist-info/RECORD +40 -0
- abstractgateway-0.1.0.dist-info/METADATA +0 -101
- abstractgateway-0.1.0.dist-info/RECORD +0 -18
- {abstractgateway-0.1.0.dist-info → abstractgateway-0.1.1.dist-info}/WHEEL +0 -0
- {abstractgateway-0.1.0.dist-info → abstractgateway-0.1.1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _safe_json_loads(line: str) -> Optional[Dict[str, Any]]:
|
|
9
|
+
try:
|
|
10
|
+
obj = json.loads(line)
|
|
11
|
+
except Exception:
|
|
12
|
+
return None
|
|
13
|
+
if not isinstance(obj, dict):
|
|
14
|
+
return None
|
|
15
|
+
return obj
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def migrate_file_to_sqlite(*, base_dir: Path, db_path: Path, overwrite: bool = False) -> None:
|
|
19
|
+
"""Best-effort migration from file-backed stores to SQLite-backed stores.
|
|
20
|
+
|
|
21
|
+
This is intended for local testing and single-host deployments.
|
|
22
|
+
|
|
23
|
+
Migrates:
|
|
24
|
+
- runs: `run_*.json` -> `runs` table (+ wait_index)
|
|
25
|
+
- ledger: `ledger_*.jsonl` -> `ledger` table (+ ledger_heads)
|
|
26
|
+
- inbox: `commands.jsonl` -> `commands` table
|
|
27
|
+
- cursor: `commands_cursor.json` -> `command_cursors` row (consumer_id=gateway_runner)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
base = Path(base_dir).expanduser().resolve()
|
|
31
|
+
if not base.exists():
|
|
32
|
+
raise FileNotFoundError(f"base_dir does not exist: {base}")
|
|
33
|
+
|
|
34
|
+
db_file = Path(db_path).expanduser().resolve()
|
|
35
|
+
if db_file.exists():
|
|
36
|
+
if not overwrite:
|
|
37
|
+
raise FileExistsError(f"Destination DB already exists: {db_file} (use --overwrite to replace)")
|
|
38
|
+
db_file.unlink()
|
|
39
|
+
|
|
40
|
+
from abstractruntime import JsonFileRunStore, SqliteDatabase, SqliteRunStore
|
|
41
|
+
|
|
42
|
+
db = SqliteDatabase(db_file)
|
|
43
|
+
src_runs = JsonFileRunStore(base)
|
|
44
|
+
dst_runs = SqliteRunStore(db)
|
|
45
|
+
|
|
46
|
+
# --- Runs ---
|
|
47
|
+
for p in sorted(base.glob("run_*.json")):
|
|
48
|
+
run_id = p.stem[len("run_") :] if p.stem.startswith("run_") else ""
|
|
49
|
+
if not run_id:
|
|
50
|
+
continue
|
|
51
|
+
run = src_runs.load(run_id)
|
|
52
|
+
if run is None:
|
|
53
|
+
continue
|
|
54
|
+
dst_runs.save(run)
|
|
55
|
+
|
|
56
|
+
conn = db.connection()
|
|
57
|
+
|
|
58
|
+
# --- Ledger ---
|
|
59
|
+
for p in sorted(base.glob("ledger_*.jsonl")):
|
|
60
|
+
run_id = p.stem[len("ledger_") :] if p.stem.startswith("ledger_") else ""
|
|
61
|
+
if not run_id:
|
|
62
|
+
continue
|
|
63
|
+
seq = 0
|
|
64
|
+
with conn:
|
|
65
|
+
with p.open("r", encoding="utf-8") as f:
|
|
66
|
+
for line in f:
|
|
67
|
+
line = line.strip()
|
|
68
|
+
if not line:
|
|
69
|
+
continue
|
|
70
|
+
obj = _safe_json_loads(line)
|
|
71
|
+
if obj is None:
|
|
72
|
+
continue
|
|
73
|
+
seq += 1
|
|
74
|
+
conn.execute(
|
|
75
|
+
"INSERT INTO ledger (run_id, seq, record_json) VALUES (?, ?, ?);",
|
|
76
|
+
(run_id, int(seq), json.dumps(obj, ensure_ascii=False)),
|
|
77
|
+
)
|
|
78
|
+
if seq > 0:
|
|
79
|
+
conn.execute(
|
|
80
|
+
"""
|
|
81
|
+
INSERT INTO ledger_heads (run_id, last_seq)
|
|
82
|
+
VALUES (?, ?)
|
|
83
|
+
ON CONFLICT(run_id) DO UPDATE SET last_seq=excluded.last_seq;
|
|
84
|
+
""",
|
|
85
|
+
(run_id, int(seq)),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# --- Commands inbox (optional, best-effort) ---
|
|
89
|
+
commands_path = base / "commands.jsonl"
|
|
90
|
+
if commands_path.exists():
|
|
91
|
+
with conn:
|
|
92
|
+
with commands_path.open("r", encoding="utf-8") as f:
|
|
93
|
+
for line in f:
|
|
94
|
+
line = line.strip()
|
|
95
|
+
if not line:
|
|
96
|
+
continue
|
|
97
|
+
obj = _safe_json_loads(line)
|
|
98
|
+
if obj is None:
|
|
99
|
+
continue
|
|
100
|
+
try:
|
|
101
|
+
seq = int(obj.get("seq") or 0)
|
|
102
|
+
except Exception:
|
|
103
|
+
seq = 0
|
|
104
|
+
command_id = str(obj.get("command_id") or "").strip()
|
|
105
|
+
run_id = str(obj.get("run_id") or "").strip()
|
|
106
|
+
typ = str(obj.get("type") or "").strip()
|
|
107
|
+
payload = obj.get("payload") if isinstance(obj.get("payload"), dict) else {}
|
|
108
|
+
ts = str(obj.get("ts") or "").strip()
|
|
109
|
+
client_id = str(obj.get("client_id") or "").strip() or None
|
|
110
|
+
|
|
111
|
+
if not command_id or not run_id or not typ or not ts:
|
|
112
|
+
continue
|
|
113
|
+
if seq <= 0:
|
|
114
|
+
continue
|
|
115
|
+
|
|
116
|
+
conn.execute(
|
|
117
|
+
"""
|
|
118
|
+
INSERT OR IGNORE INTO commands (seq, command_id, run_id, type, payload_json, ts, client_id)
|
|
119
|
+
VALUES (?, ?, ?, ?, ?, ?, ?);
|
|
120
|
+
""",
|
|
121
|
+
(
|
|
122
|
+
int(seq),
|
|
123
|
+
command_id,
|
|
124
|
+
run_id,
|
|
125
|
+
typ,
|
|
126
|
+
json.dumps(payload, ensure_ascii=False),
|
|
127
|
+
ts,
|
|
128
|
+
client_id,
|
|
129
|
+
),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# --- Cursor ---
|
|
133
|
+
cursor_path = base / "commands_cursor.json"
|
|
134
|
+
if cursor_path.exists():
|
|
135
|
+
try:
|
|
136
|
+
cursor_obj = json.loads(cursor_path.read_text(encoding="utf-8") or "{}")
|
|
137
|
+
except Exception:
|
|
138
|
+
cursor_obj = {}
|
|
139
|
+
cur_val = cursor_obj.get("cursor")
|
|
140
|
+
try:
|
|
141
|
+
cur_i = int(cur_val or 0)
|
|
142
|
+
except Exception:
|
|
143
|
+
cur_i = 0
|
|
144
|
+
with conn:
|
|
145
|
+
conn.execute(
|
|
146
|
+
"""
|
|
147
|
+
INSERT INTO command_cursors (consumer_id, cursor, updated_at)
|
|
148
|
+
VALUES (?, ?, ?)
|
|
149
|
+
ON CONFLICT(consumer_id) DO UPDATE SET
|
|
150
|
+
cursor=excluded.cursor,
|
|
151
|
+
updated_at=excluded.updated_at;
|
|
152
|
+
""",
|
|
153
|
+
("gateway_runner", int(cur_i), str(cursor_obj.get("updated_at") or "")),
|
|
154
|
+
)
|
|
155
|
+
|