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.
Files changed (40) hide show
  1. abstractgateway/__init__.py +1 -2
  2. abstractgateway/__main__.py +7 -0
  3. abstractgateway/app.py +4 -4
  4. abstractgateway/cli.py +568 -8
  5. abstractgateway/config.py +15 -5
  6. abstractgateway/embeddings_config.py +45 -0
  7. abstractgateway/host_metrics.py +274 -0
  8. abstractgateway/hosts/bundle_host.py +528 -55
  9. abstractgateway/hosts/visualflow_host.py +30 -3
  10. abstractgateway/integrations/__init__.py +2 -0
  11. abstractgateway/integrations/email_bridge.py +782 -0
  12. abstractgateway/integrations/telegram_bridge.py +534 -0
  13. abstractgateway/maintenance/__init__.py +5 -0
  14. abstractgateway/maintenance/action_tokens.py +100 -0
  15. abstractgateway/maintenance/backlog_exec_runner.py +1592 -0
  16. abstractgateway/maintenance/backlog_parser.py +184 -0
  17. abstractgateway/maintenance/draft_generator.py +451 -0
  18. abstractgateway/maintenance/llm_assist.py +212 -0
  19. abstractgateway/maintenance/notifier.py +109 -0
  20. abstractgateway/maintenance/process_manager.py +1064 -0
  21. abstractgateway/maintenance/report_models.py +81 -0
  22. abstractgateway/maintenance/report_parser.py +219 -0
  23. abstractgateway/maintenance/text_similarity.py +123 -0
  24. abstractgateway/maintenance/triage.py +507 -0
  25. abstractgateway/maintenance/triage_queue.py +142 -0
  26. abstractgateway/migrate.py +155 -0
  27. abstractgateway/routes/__init__.py +2 -2
  28. abstractgateway/routes/gateway.py +10817 -179
  29. abstractgateway/routes/triage.py +118 -0
  30. abstractgateway/runner.py +689 -14
  31. abstractgateway/security/gateway_security.py +425 -110
  32. abstractgateway/service.py +213 -6
  33. abstractgateway/stores.py +64 -4
  34. abstractgateway/workflow_deprecations.py +225 -0
  35. abstractgateway-0.1.1.dist-info/METADATA +135 -0
  36. abstractgateway-0.1.1.dist-info/RECORD +40 -0
  37. abstractgateway-0.1.0.dist-info/METADATA +0 -101
  38. abstractgateway-0.1.0.dist-info/RECORD +0 -18
  39. {abstractgateway-0.1.0.dist-info → abstractgateway-0.1.1.dist-info}/WHEEL +0 -0
  40. {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
+
@@ -1,5 +1,5 @@
1
1
  from .gateway import router as gateway_router
2
+ from .triage import router as triage_router
2
3
 
3
- __all__ = ["gateway_router"]
4
-
4
+ __all__ = ["gateway_router", "triage_router"]
5
5