delimit-cli 3.14.28 → 3.14.29
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/gateway/ai/backends/deploy_bridge.py +56 -2
- package/gateway/ai/backends/gateway_core.py +212 -1
- package/gateway/ai/backends/generate_bridge.py +84 -13
- package/gateway/ai/backends/governance_bridge.py +63 -16
- package/gateway/ai/backends/memory_bridge.py +77 -76
- package/gateway/ai/backends/ops_bridge.py +76 -6
- package/gateway/ai/backends/os_bridge.py +23 -3
- package/gateway/ai/backends/repo_bridge.py +156 -17
- package/gateway/ai/backends/tools_design.py +116 -9
- package/gateway/ai/backends/tools_infra.py +200 -72
- package/gateway/ai/backends/tools_real.py +8 -0
- package/gateway/ai/backends/ui_bridge.py +115 -5
- package/gateway/ai/backends/vault_bridge.py +69 -114
- package/gateway/ai/content_engine.py +1276 -0
- package/gateway/ai/context_fs.py +193 -0
- package/gateway/ai/daemon.py +500 -0
- package/gateway/ai/data_plane.py +291 -0
- package/gateway/ai/deliberation.py +1033 -6
- package/gateway/ai/events.py +39 -0
- package/gateway/ai/founding_users.py +162 -0
- package/gateway/ai/governance.py +698 -4
- package/gateway/ai/inbox_daemon.py +78 -17
- package/gateway/ai/integrations/__init__.py +1 -0
- package/gateway/ai/integrations/opensage_wrapper.py +288 -0
- package/gateway/ai/key_resolver.py +95 -0
- package/gateway/ai/ledger_manager.py +289 -1
- package/gateway/ai/license.py +62 -4
- package/gateway/ai/license_core.py +208 -7
- package/gateway/ai/local_server.py +215 -0
- package/gateway/ai/loop_engine.py +408 -0
- package/gateway/ai/mcp_bridge.py +178 -0
- package/gateway/ai/release_sync.py +2 -2
- package/gateway/ai/screen_record.py +374 -0
- package/gateway/ai/secrets_broker.py +235 -0
- package/gateway/ai/social.py +189 -27
- package/gateway/ai/social_target.py +1635 -0
- package/gateway/ai/supabase_sync.py +190 -0
- package/gateway/ai/tracing.py +195 -0
- package/gateway/core/contract_ledger.py +1 -1
- package/gateway/core/dependency_graph.py +1 -1
- package/gateway/core/dependency_manifest.py +1 -1
- package/gateway/core/diff_engine_v2.py +272 -78
- package/gateway/core/event_backbone.py +2 -2
- package/gateway/core/event_schema.py +1 -1
- package/gateway/core/impact_analyzer.py +1 -1
- package/gateway/core/policy_engine.py +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""Data/Action Plane — external systems as typed mounted resources.
|
|
2
|
+
|
|
3
|
+
Instead of ad hoc API calls, external systems appear as structured resources
|
|
4
|
+
with schemas, permissions, and transactional operations. Like device drivers.
|
|
5
|
+
|
|
6
|
+
STR-050: First driver is GitHub (repos, PRs, issues, workflows).
|
|
7
|
+
"""
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import subprocess
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
from typing import Optional, List, Dict, Any
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GitHubDriver:
|
|
17
|
+
"""GitHub as a typed mounted resource."""
|
|
18
|
+
|
|
19
|
+
def __init__(self):
|
|
20
|
+
self.gh_available = bool(
|
|
21
|
+
subprocess.run(["which", "gh"], capture_output=True).returncode == 0
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def _gh(self, args: list, parse_json: bool = True) -> dict:
|
|
25
|
+
"""Run a gh CLI command."""
|
|
26
|
+
if not self.gh_available:
|
|
27
|
+
return {"error": "GitHub CLI (gh) not available"}
|
|
28
|
+
try:
|
|
29
|
+
result = subprocess.run(
|
|
30
|
+
["gh"] + args,
|
|
31
|
+
capture_output=True,
|
|
32
|
+
text=True,
|
|
33
|
+
timeout=30,
|
|
34
|
+
)
|
|
35
|
+
if result.returncode != 0:
|
|
36
|
+
return {"error": result.stderr.strip()}
|
|
37
|
+
if parse_json and result.stdout.strip():
|
|
38
|
+
return json.loads(result.stdout)
|
|
39
|
+
return {"output": result.stdout.strip()}
|
|
40
|
+
except subprocess.TimeoutExpired:
|
|
41
|
+
return {"error": "Command timed out after 30s"}
|
|
42
|
+
except json.JSONDecodeError as e:
|
|
43
|
+
return {"error": f"Failed to parse JSON: {e}"}
|
|
44
|
+
except Exception as e:
|
|
45
|
+
return {"error": str(e)}
|
|
46
|
+
|
|
47
|
+
# --- Repos ---
|
|
48
|
+
|
|
49
|
+
def list_repos(self, org: str = "", limit: int = 20) -> list:
|
|
50
|
+
"""List repositories."""
|
|
51
|
+
args = [
|
|
52
|
+
"repo",
|
|
53
|
+
"list",
|
|
54
|
+
"--json",
|
|
55
|
+
"name,description,url,isPrivate,stargazerCount,updatedAt",
|
|
56
|
+
"--limit",
|
|
57
|
+
str(limit),
|
|
58
|
+
]
|
|
59
|
+
if org:
|
|
60
|
+
args.insert(2, org)
|
|
61
|
+
result = self._gh(args)
|
|
62
|
+
return result if isinstance(result, list) else [result]
|
|
63
|
+
|
|
64
|
+
def get_repo(self, repo: str) -> dict:
|
|
65
|
+
"""Get repository details."""
|
|
66
|
+
return self._gh(
|
|
67
|
+
[
|
|
68
|
+
"repo",
|
|
69
|
+
"view",
|
|
70
|
+
repo,
|
|
71
|
+
"--json",
|
|
72
|
+
"name,description,url,defaultBranchRef,isPrivate,stargazerCount,issues,pullRequests",
|
|
73
|
+
]
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# --- Pull Requests ---
|
|
77
|
+
|
|
78
|
+
def list_prs(self, repo: str = "", state: str = "open", limit: int = 10) -> list:
|
|
79
|
+
"""List pull requests."""
|
|
80
|
+
args = [
|
|
81
|
+
"pr",
|
|
82
|
+
"list",
|
|
83
|
+
"--json",
|
|
84
|
+
"number,title,state,author,createdAt,url,labels",
|
|
85
|
+
"--state",
|
|
86
|
+
state,
|
|
87
|
+
"--limit",
|
|
88
|
+
str(limit),
|
|
89
|
+
]
|
|
90
|
+
if repo:
|
|
91
|
+
args.extend(["--repo", repo])
|
|
92
|
+
result = self._gh(args)
|
|
93
|
+
return result if isinstance(result, list) else [result]
|
|
94
|
+
|
|
95
|
+
def get_pr(self, repo: str, number: int) -> dict:
|
|
96
|
+
"""Get PR details with comments and checks."""
|
|
97
|
+
return self._gh(
|
|
98
|
+
[
|
|
99
|
+
"pr",
|
|
100
|
+
"view",
|
|
101
|
+
str(number),
|
|
102
|
+
"--repo",
|
|
103
|
+
repo,
|
|
104
|
+
"--json",
|
|
105
|
+
"number,title,body,state,author,comments,statusCheckRollup,files,additions,deletions",
|
|
106
|
+
]
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def create_pr(
|
|
110
|
+
self, repo: str, title: str, body: str, head: str, base: str = "main"
|
|
111
|
+
) -> dict:
|
|
112
|
+
"""Create a pull request."""
|
|
113
|
+
return self._gh(
|
|
114
|
+
[
|
|
115
|
+
"pr",
|
|
116
|
+
"create",
|
|
117
|
+
"--repo",
|
|
118
|
+
repo,
|
|
119
|
+
"--title",
|
|
120
|
+
title,
|
|
121
|
+
"--body",
|
|
122
|
+
body,
|
|
123
|
+
"--head",
|
|
124
|
+
head,
|
|
125
|
+
"--base",
|
|
126
|
+
base,
|
|
127
|
+
"--json",
|
|
128
|
+
"number,url",
|
|
129
|
+
]
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# --- Issues ---
|
|
133
|
+
|
|
134
|
+
def list_issues(
|
|
135
|
+
self, repo: str = "", state: str = "open", limit: int = 10
|
|
136
|
+
) -> list:
|
|
137
|
+
"""List issues."""
|
|
138
|
+
args = [
|
|
139
|
+
"issue",
|
|
140
|
+
"list",
|
|
141
|
+
"--json",
|
|
142
|
+
"number,title,state,author,createdAt,url,labels",
|
|
143
|
+
"--state",
|
|
144
|
+
state,
|
|
145
|
+
"--limit",
|
|
146
|
+
str(limit),
|
|
147
|
+
]
|
|
148
|
+
if repo:
|
|
149
|
+
args.extend(["--repo", repo])
|
|
150
|
+
result = self._gh(args)
|
|
151
|
+
return result if isinstance(result, list) else [result]
|
|
152
|
+
|
|
153
|
+
def create_issue(self, repo: str, title: str, body: str) -> dict:
|
|
154
|
+
"""Create an issue."""
|
|
155
|
+
return self._gh(
|
|
156
|
+
[
|
|
157
|
+
"issue",
|
|
158
|
+
"create",
|
|
159
|
+
"--repo",
|
|
160
|
+
repo,
|
|
161
|
+
"--title",
|
|
162
|
+
title,
|
|
163
|
+
"--body",
|
|
164
|
+
body,
|
|
165
|
+
"--json",
|
|
166
|
+
"number,url",
|
|
167
|
+
]
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
def get_issue(self, repo: str, number: int) -> dict:
|
|
171
|
+
"""Get issue details with comments."""
|
|
172
|
+
return self._gh(
|
|
173
|
+
[
|
|
174
|
+
"issue",
|
|
175
|
+
"view",
|
|
176
|
+
str(number),
|
|
177
|
+
"--repo",
|
|
178
|
+
repo,
|
|
179
|
+
"--json",
|
|
180
|
+
"number,title,body,state,author,comments,labels",
|
|
181
|
+
]
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# --- Workflows ---
|
|
185
|
+
|
|
186
|
+
def list_runs(self, repo: str = "", limit: int = 5) -> list:
|
|
187
|
+
"""List recent workflow runs."""
|
|
188
|
+
if not repo:
|
|
189
|
+
return [{"error": "repo is required for workflow runs"}]
|
|
190
|
+
result = self._gh(
|
|
191
|
+
[
|
|
192
|
+
"run",
|
|
193
|
+
"list",
|
|
194
|
+
"--repo",
|
|
195
|
+
repo,
|
|
196
|
+
"--json",
|
|
197
|
+
"databaseId,displayTitle,status,conclusion,createdAt,url",
|
|
198
|
+
"--limit",
|
|
199
|
+
str(limit),
|
|
200
|
+
]
|
|
201
|
+
)
|
|
202
|
+
return result if isinstance(result, list) else [result]
|
|
203
|
+
|
|
204
|
+
def get_run(self, repo: str, run_id: int) -> dict:
|
|
205
|
+
"""Get workflow run details."""
|
|
206
|
+
return self._gh(
|
|
207
|
+
[
|
|
208
|
+
"run",
|
|
209
|
+
"view",
|
|
210
|
+
str(run_id),
|
|
211
|
+
"--repo",
|
|
212
|
+
repo,
|
|
213
|
+
"--json",
|
|
214
|
+
"databaseId,displayTitle,status,conclusion,jobs",
|
|
215
|
+
]
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# --- Resource Schema ---
|
|
219
|
+
|
|
220
|
+
@staticmethod
|
|
221
|
+
def schema() -> dict:
|
|
222
|
+
"""Return the typed resource schema for GitHub."""
|
|
223
|
+
return {
|
|
224
|
+
"driver": "github",
|
|
225
|
+
"resources": {
|
|
226
|
+
"repos": {
|
|
227
|
+
"operations": ["list", "get"],
|
|
228
|
+
"fields": [
|
|
229
|
+
"name",
|
|
230
|
+
"description",
|
|
231
|
+
"url",
|
|
232
|
+
"isPrivate",
|
|
233
|
+
"stars",
|
|
234
|
+
],
|
|
235
|
+
},
|
|
236
|
+
"pull_requests": {
|
|
237
|
+
"operations": ["list", "get", "create"],
|
|
238
|
+
"fields": [
|
|
239
|
+
"number",
|
|
240
|
+
"title",
|
|
241
|
+
"state",
|
|
242
|
+
"author",
|
|
243
|
+
"files",
|
|
244
|
+
],
|
|
245
|
+
},
|
|
246
|
+
"issues": {
|
|
247
|
+
"operations": ["list", "get", "create"],
|
|
248
|
+
"fields": [
|
|
249
|
+
"number",
|
|
250
|
+
"title",
|
|
251
|
+
"state",
|
|
252
|
+
"author",
|
|
253
|
+
"labels",
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
"workflows": {
|
|
257
|
+
"operations": ["list_runs", "get_run"],
|
|
258
|
+
"fields": [
|
|
259
|
+
"id",
|
|
260
|
+
"title",
|
|
261
|
+
"status",
|
|
262
|
+
"conclusion",
|
|
263
|
+
],
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# Registry of available drivers
|
|
270
|
+
DRIVERS: Dict[str, type] = {
|
|
271
|
+
"github": GitHubDriver,
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def get_driver(name: str) -> Optional[Any]:
|
|
276
|
+
"""Get a data plane driver by name."""
|
|
277
|
+
cls = DRIVERS.get(name)
|
|
278
|
+
if not cls:
|
|
279
|
+
return None
|
|
280
|
+
return cls()
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def list_drivers() -> list:
|
|
284
|
+
"""List all available data plane drivers."""
|
|
285
|
+
return [
|
|
286
|
+
{
|
|
287
|
+
"name": name,
|
|
288
|
+
"schema": cls.schema() if hasattr(cls, "schema") else {},
|
|
289
|
+
}
|
|
290
|
+
for name, cls in DRIVERS.items()
|
|
291
|
+
]
|