darkfire514 commited on
Commit
7dd9384
·
verified ·
1 Parent(s): 6d8ba32

Upload 193 files

Browse files
Files changed (34) hide show
  1. app.py +29 -0
  2. openspace/__pycache__/__init__.cpython-311.pyc +0 -0
  3. openspace/__pycache__/dashboard_server.cpython-311.pyc +0 -0
  4. openspace/__pycache__/mcp_server.cpython-311.pyc +0 -0
  5. openspace/cloud/__pycache__/__init__.cpython-311.pyc +0 -0
  6. openspace/cloud/__pycache__/auth.cpython-311.pyc +0 -0
  7. openspace/cloud/__pycache__/client.cpython-311.pyc +0 -0
  8. openspace/cloud/cli/__pycache__/__init__.cpython-311.pyc +0 -0
  9. openspace/cloud/cli/__pycache__/upload_skill.cpython-311.pyc +0 -0
  10. openspace/config/__pycache__/__init__.cpython-311.pyc +0 -0
  11. openspace/config/__pycache__/constants.cpython-311.pyc +0 -0
  12. openspace/config/__pycache__/grounding.cpython-311.pyc +0 -0
  13. openspace/config/__pycache__/loader.cpython-311.pyc +0 -0
  14. openspace/config/__pycache__/utils.cpython-311.pyc +0 -0
  15. openspace/dashboard_server.py +61 -4
  16. openspace/grounding/core/__pycache__/types.cpython-311.pyc +0 -0
  17. openspace/host_detection/__pycache__/__init__.cpython-311.pyc +0 -0
  18. openspace/host_detection/__pycache__/nanobot.cpython-311.pyc +0 -0
  19. openspace/host_detection/__pycache__/openclaw.cpython-311.pyc +0 -0
  20. openspace/host_detection/__pycache__/resolver.cpython-311.pyc +0 -0
  21. openspace/recording/__pycache__/__init__.cpython-311.pyc +0 -0
  22. openspace/recording/__pycache__/action_recorder.cpython-311.pyc +0 -0
  23. openspace/recording/__pycache__/utils.cpython-311.pyc +0 -0
  24. openspace/skill_engine/__pycache__/__init__.cpython-311.pyc +0 -0
  25. openspace/skill_engine/__pycache__/fuzzy_match.cpython-311.pyc +0 -0
  26. openspace/skill_engine/__pycache__/patch.cpython-311.pyc +0 -0
  27. openspace/skill_engine/__pycache__/registry.cpython-311.pyc +0 -0
  28. openspace/skill_engine/__pycache__/skill_ranker.cpython-311.pyc +0 -0
  29. openspace/skill_engine/__pycache__/skill_utils.cpython-311.pyc +0 -0
  30. openspace/skill_engine/__pycache__/store.cpython-311.pyc +0 -0
  31. openspace/skill_engine/__pycache__/types.cpython-311.pyc +0 -0
  32. openspace/skill_engine/store.py +36 -117
  33. openspace/utils/__pycache__/logging.cpython-311.pyc +0 -0
  34. requirements.txt +1 -1
app.py CHANGED
@@ -1,5 +1,34 @@
1
  import os
 
 
2
  from openspace.dashboard_server import create_app
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
  app = create_app()
5
 
 
1
  import os
2
+ import asyncio
3
+ from pathlib import Path
4
  from openspace.dashboard_server import create_app
5
+ from openspace.skill_engine.registry import SkillRegistry
6
+ from openspace.skill_engine.store import SkillStore
7
+
8
+ def sync_local_skills():
9
+ try:
10
+ print("Starting local skill sync...")
11
+ registry = SkillRegistry()
12
+ skill_dir = Path(__file__).resolve().parent / "skills"
13
+ if skill_dir.exists() and skill_dir.is_dir():
14
+ added = registry.discover_from_dirs([skill_dir])
15
+ if added:
16
+ store = SkillStore()
17
+ # Run the async sync method in a synchronous context
18
+ loop = asyncio.new_event_loop()
19
+ asyncio.set_event_loop(loop)
20
+ count = loop.run_until_complete(store.sync_from_registry(added))
21
+ loop.close()
22
+ print(f"Successfully synced {count} skills from {skill_dir} into database.")
23
+ else:
24
+ print("No skills discovered in directory.")
25
+ else:
26
+ print(f"Skill directory not found at {skill_dir}")
27
+ except Exception as e:
28
+ print(f"Error syncing local skills: {e}")
29
+
30
+ # Run sync before creating the app
31
+ sync_local_skills()
32
 
33
  app = create_app()
34
 
openspace/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (2.5 kB). View file
 
openspace/__pycache__/dashboard_server.cpython-311.pyc ADDED
Binary file (44.9 kB). View file
 
openspace/__pycache__/mcp_server.cpython-311.pyc ADDED
Binary file (44.6 kB). View file
 
openspace/cloud/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (1.27 kB). View file
 
openspace/cloud/__pycache__/auth.cpython-311.pyc ADDED
Binary file (4.79 kB). View file
 
openspace/cloud/__pycache__/client.cpython-311.pyc ADDED
Binary file (25.9 kB). View file
 
openspace/cloud/cli/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (174 Bytes). View file
 
openspace/cloud/cli/__pycache__/upload_skill.cpython-311.pyc ADDED
Binary file (5.93 kB). View file
 
openspace/config/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (672 Bytes). View file
 
openspace/config/__pycache__/constants.cpython-311.pyc ADDED
Binary file (690 Bytes). View file
 
openspace/config/__pycache__/grounding.cpython-311.pyc ADDED
Binary file (18.2 kB). View file
 
openspace/config/__pycache__/loader.cpython-311.pyc ADDED
Binary file (8.7 kB). View file
 
openspace/config/__pycache__/utils.cpython-311.pyc ADDED
Binary file (2.22 kB). View file
 
openspace/dashboard_server.py CHANGED
@@ -234,8 +234,18 @@ def create_app() -> Flask:
234
  if not stage_dir.exists():
235
  abort(404, description="Artifact not found")
236
 
237
- # The skill folder name should ideally be the skill name
238
- skill_name = data.get("name", artifact_id)
 
 
 
 
 
 
 
 
 
 
239
  final_dir = PROJECT_ROOT / "skills" / skill_name
240
 
241
  # If final_dir exists, we might need to overwrite or abort
@@ -260,21 +270,68 @@ def create_app() -> Flask:
260
 
261
  store = _get_store()
262
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  # Sync to DB
264
  import asyncio
265
  loop = asyncio.new_event_loop()
266
  asyncio.set_event_loop(loop)
267
  try:
268
- loop.run_until_complete(store.sync_from_registry([meta]))
269
  finally:
270
  loop.close()
271
 
272
  return jsonify({
273
  "status": "success",
 
274
  "skill_id": meta.skill_id,
275
  "name": meta.name,
276
  "local_path": str(final_dir)
277
- })
278
 
279
  @app.route(f"{API_PREFIX}/workflows", methods=["GET"])
280
  def list_workflows() -> Any:
 
234
  if not stage_dir.exists():
235
  abort(404, description="Artifact not found")
236
 
237
+ from openspace.skill_engine.skill_utils import parse_frontmatter
238
+ skill_file = stage_dir / "SKILL.md"
239
+ if not skill_file.exists():
240
+ abort(400, description="Missing SKILL.md")
241
+
242
+ try:
243
+ content = skill_file.read_text(encoding="utf-8")
244
+ fm = parse_frontmatter(content)
245
+ skill_name = fm.get("name", artifact_id)
246
+ except Exception:
247
+ skill_name = artifact_id
248
+
249
  final_dir = PROJECT_ROOT / "skills" / skill_name
250
 
251
  # If final_dir exists, we might need to overwrite or abort
 
270
 
271
  store = _get_store()
272
 
273
+ from openspace.skill_engine.types import SkillRecord, SkillLineage, SkillOrigin, SkillVisibility
274
+ from openspace.skill_engine.patch import collect_skill_snapshot, compute_unified_diff
275
+
276
+ try:
277
+ origin_val = data.get("origin", "imported").upper()
278
+ origin = SkillOrigin[origin_val]
279
+ except KeyError:
280
+ origin = SkillOrigin.IMPORTED
281
+
282
+ try:
283
+ vis_val = data.get("visibility", "public").upper()
284
+ if vis_val == "GROUP_ONLY":
285
+ vis_val = "PRIVATE"
286
+ visibility = SkillVisibility[vis_val]
287
+ except KeyError:
288
+ visibility = SkillVisibility.PUBLIC
289
+
290
+ snapshot = {}
291
+ content_diff = ""
292
+ try:
293
+ snapshot = collect_skill_snapshot(final_dir)
294
+ content_diff = "\n".join(
295
+ compute_unified_diff("", text, filename=name)
296
+ for name, text in sorted(snapshot.items())
297
+ if compute_unified_diff("", text, filename=name)
298
+ )
299
+ except Exception:
300
+ pass
301
+
302
+ record = SkillRecord(
303
+ skill_id=meta.skill_id,
304
+ name=meta.name,
305
+ description=meta.description,
306
+ path=str(meta.path),
307
+ is_active=True,
308
+ visibility=visibility,
309
+ tags=set(data.get("tags", [])),
310
+ lineage=SkillLineage(
311
+ origin=origin,
312
+ generation=0,
313
+ parent_skill_ids=data.get("parent_skill_ids", []),
314
+ content_snapshot=snapshot,
315
+ content_diff=content_diff,
316
+ ),
317
+ )
318
+
319
  # Sync to DB
320
  import asyncio
321
  loop = asyncio.new_event_loop()
322
  asyncio.set_event_loop(loop)
323
  try:
324
+ loop.run_until_complete(store.save_record(record))
325
  finally:
326
  loop.close()
327
 
328
  return jsonify({
329
  "status": "success",
330
+ "record_id": meta.skill_id,
331
  "skill_id": meta.skill_id,
332
  "name": meta.name,
333
  "local_path": str(final_dir)
334
+ }), 201
335
 
336
  @app.route(f"{API_PREFIX}/workflows", methods=["GET"])
337
  def list_workflows() -> Any:
openspace/grounding/core/__pycache__/types.cpython-311.pyc ADDED
Binary file (15.1 kB). View file
 
openspace/host_detection/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (3.17 kB). View file
 
openspace/host_detection/__pycache__/nanobot.cpython-311.pyc ADDED
Binary file (9.95 kB). View file
 
openspace/host_detection/__pycache__/openclaw.cpython-311.pyc ADDED
Binary file (6.47 kB). View file
 
openspace/host_detection/__pycache__/resolver.cpython-311.pyc ADDED
Binary file (9.78 kB). View file
 
openspace/recording/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (1.83 kB). View file
 
openspace/recording/__pycache__/action_recorder.cpython-311.pyc ADDED
Binary file (12.4 kB). View file
 
openspace/recording/__pycache__/utils.cpython-311.pyc ADDED
Binary file (24 kB). View file
 
openspace/skill_engine/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (1.93 kB). View file
 
openspace/skill_engine/__pycache__/fuzzy_match.cpython-311.pyc ADDED
Binary file (17.2 kB). View file
 
openspace/skill_engine/__pycache__/patch.cpython-311.pyc ADDED
Binary file (47.1 kB). View file
 
openspace/skill_engine/__pycache__/registry.cpython-311.pyc ADDED
Binary file (36.6 kB). View file
 
openspace/skill_engine/__pycache__/skill_ranker.cpython-311.pyc ADDED
Binary file (20.5 kB). View file
 
openspace/skill_engine/__pycache__/skill_utils.cpython-311.pyc ADDED
Binary file (15.3 kB). View file
 
openspace/skill_engine/__pycache__/store.cpython-311.pyc ADDED
Binary file (76 kB). View file
 
openspace/skill_engine/__pycache__/types.cpython-311.pyc ADDED
Binary file (25.5 kB). View file
 
openspace/skill_engine/store.py CHANGED
@@ -13,8 +13,7 @@ from __future__ import annotations
13
 
14
  import asyncio
15
  import json
16
- import os
17
- import libsql_experimental as sqlite3
18
  import threading
19
  import time
20
  from contextlib import contextmanager
@@ -58,26 +57,19 @@ def _db_retry(
58
  for attempt in range(max_retries):
59
  try:
60
  return func(*args, **kwargs)
61
- except Exception as exc:
62
- # libsql_experimental might not have standard sqlite3 exceptions
63
- # so we catch Exception and check if it's a known database error string
64
- exc_str = str(exc).lower()
65
- if "locked" in exc_str or "database error" in exc_str or "operational error" in exc_str:
66
- if attempt == max_retries - 1:
67
- logger.error(
68
- f"DB {func.__name__} failed after "
69
- f"{max_retries} retries: {exc}"
70
- )
71
- raise
72
- logger.warning(
73
- f"DB {func.__name__} retry {attempt + 1}"
74
- f"/{max_retries}: {exc}"
75
  )
76
- time.sleep(delay)
77
- delay *= backoff
78
- else:
79
- # If it's not a known transient error, raise immediately
80
  raise
 
 
 
 
 
 
81
 
82
  return wrapper
83
 
@@ -173,69 +165,6 @@ CREATE TABLE IF NOT EXISTS skill_tags (
173
  """
174
 
175
 
176
- class _DictCursorWrapper:
177
- def __init__(self, cursor):
178
- self._cursor = cursor
179
-
180
- def __getattr__(self, name):
181
- return getattr(self._cursor, name)
182
-
183
- def _dict_factory(self, row):
184
- if row is None:
185
- return None
186
- if self._cursor.description is None:
187
- return row
188
- fields = [column[0] for column in self._cursor.description]
189
- return {key: value for key, value in zip(fields, row)}
190
-
191
- def fetchone(self):
192
- return self._dict_factory(self._cursor.fetchone())
193
-
194
- def fetchall(self):
195
- rows = self._cursor.fetchall()
196
- if not rows:
197
- return []
198
- return [self._dict_factory(row) for row in rows]
199
-
200
- def fetchmany(self, size=None):
201
- rows = self._cursor.fetchmany(size) if size is not None else self._cursor.fetchmany()
202
- if not rows:
203
- return []
204
- return [self._dict_factory(row) for row in rows]
205
-
206
- def __iter__(self):
207
- for row in self._cursor:
208
- yield self._dict_factory(row)
209
-
210
- class _DictConnectionWrapper:
211
- def __init__(self, conn):
212
- self._conn = conn
213
-
214
- def __getattr__(self, name):
215
- return getattr(self._conn, name)
216
-
217
- def cursor(self):
218
- return _DictCursorWrapper(self._conn.cursor())
219
-
220
- def execute(self, *args, **kwargs):
221
- return _DictCursorWrapper(self._conn.execute(*args, **kwargs))
222
-
223
- def executemany(self, *args, **kwargs):
224
- return _DictCursorWrapper(self._conn.executemany(*args, **kwargs))
225
-
226
- def executescript(self, *args, **kwargs):
227
- return _DictCursorWrapper(self._conn.executescript(*args, **kwargs))
228
-
229
- def __enter__(self):
230
- if hasattr(self._conn, '__enter__'):
231
- self._conn.__enter__()
232
- return self
233
-
234
- def __exit__(self, exc_type, exc_val, exc_tb):
235
- if hasattr(self._conn, '__exit__'):
236
- return self._conn.__exit__(exc_type, exc_val, exc_tb)
237
- return False
238
-
239
  class SkillStore:
240
  """SQLite persistence engine — Skill quality tracking and evolution ledger.
241
 
@@ -276,29 +205,21 @@ class SkillStore:
276
 
277
  Read connection: ``query_only=ON`` pragma for safety.
278
  """
279
- db_url = os.environ.get("TURSO_DATABASE_URL")
280
- auth_token = os.environ.get("TURSO_AUTH_TOKEN")
281
-
282
- if db_url and auth_token:
283
- conn = sqlite3.connect(db_url, auth_token=auth_token)
284
- else:
285
- conn = sqlite3.connect(
286
- str(self._db_path),
287
- timeout=30.0,
288
- check_same_thread=False,
289
- )
290
- conn.execute("PRAGMA journal_mode=WAL")
291
- conn.execute("PRAGMA busy_timeout=30000")
292
- conn.execute("PRAGMA synchronous=NORMAL")
293
- conn.execute("PRAGMA cache_size=-16000") # 16 MB
294
- conn.execute("PRAGMA temp_store=MEMORY")
295
- conn.execute("PRAGMA foreign_keys=ON")
296
- if read_only:
297
- conn.execute("PRAGMA query_only=ON")
298
-
299
- # libsql-experimental doesn't support setting row_factory directly on the connection
300
- # We wrap it to handle row-to-dict conversion manually where cursor.fetchall() is called
301
- return _DictConnectionWrapper(conn)
302
 
303
  @contextmanager
304
  def _reader(self) -> Generator[sqlite3.Connection, None, None]:
@@ -357,9 +278,7 @@ class SkillStore:
357
  self._closed = True
358
  try:
359
  # Flush WAL → main DB so external readers see all data
360
- db_url = os.environ.get("TURSO_DATABASE_URL")
361
- if not db_url:
362
- self._conn.execute("PRAGMA wal_checkpoint(TRUNCATE)")
363
  self._conn.close()
364
  except Exception:
365
  pass
@@ -978,8 +897,8 @@ class SkillStore:
978
  with self._reader() as conn:
979
  where = " WHERE is_active=1" if active_only else ""
980
  total = conn.execute(
981
- f"SELECT COUNT(*) as count FROM skill_records{where}"
982
- ).fetchone()["count"]
983
 
984
  by_category = {
985
  r["category"]: r["cnt"]
@@ -996,12 +915,12 @@ class SkillStore:
996
  ).fetchall()
997
  }
998
  n_analyses = conn.execute(
999
- "SELECT COUNT(*) as count FROM execution_analyses"
1000
- ).fetchone()["count"]
1001
  n_candidates = conn.execute(
1002
- "SELECT COUNT(*) as count FROM execution_analyses "
1003
  "WHERE candidate_for_evolution=1"
1004
- ).fetchone()["count"]
1005
  agg = conn.execute(
1006
  f"""
1007
  SELECT SUM(total_selections) AS sel,
@@ -1014,8 +933,8 @@ class SkillStore:
1014
 
1015
  # Also report total (including inactive) for context
1016
  total_all = conn.execute(
1017
- "SELECT COUNT(*) as count FROM skill_records"
1018
- ).fetchone()["count"]
1019
 
1020
  return {
1021
  "total_skills": total,
 
13
 
14
  import asyncio
15
  import json
16
+ import sqlite3
 
17
  import threading
18
  import time
19
  from contextlib import contextmanager
 
57
  for attempt in range(max_retries):
58
  try:
59
  return func(*args, **kwargs)
60
+ except (sqlite3.OperationalError, sqlite3.DatabaseError) as exc:
61
+ if attempt == max_retries - 1:
62
+ logger.error(
63
+ f"DB {func.__name__} failed after "
64
+ f"{max_retries} retries: {exc}"
 
 
 
 
 
 
 
 
 
65
  )
 
 
 
 
66
  raise
67
+ logger.warning(
68
+ f"DB {func.__name__} retry {attempt + 1}"
69
+ f"/{max_retries}: {exc}"
70
+ )
71
+ time.sleep(delay)
72
+ delay *= backoff
73
 
74
  return wrapper
75
 
 
165
  """
166
 
167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  class SkillStore:
169
  """SQLite persistence engine — Skill quality tracking and evolution ledger.
170
 
 
205
 
206
  Read connection: ``query_only=ON`` pragma for safety.
207
  """
208
+ conn = sqlite3.connect(
209
+ str(self._db_path),
210
+ timeout=30.0,
211
+ check_same_thread=False,
212
+ )
213
+ conn.execute("PRAGMA journal_mode=WAL")
214
+ conn.execute("PRAGMA busy_timeout=30000")
215
+ conn.execute("PRAGMA synchronous=NORMAL")
216
+ conn.execute("PRAGMA cache_size=-16000") # 16 MB
217
+ conn.execute("PRAGMA temp_store=MEMORY")
218
+ conn.execute("PRAGMA foreign_keys=ON")
219
+ if read_only:
220
+ conn.execute("PRAGMA query_only=ON")
221
+ conn.row_factory = sqlite3.Row
222
+ return conn
 
 
 
 
 
 
 
 
223
 
224
  @contextmanager
225
  def _reader(self) -> Generator[sqlite3.Connection, None, None]:
 
278
  self._closed = True
279
  try:
280
  # Flush WAL → main DB so external readers see all data
281
+ self._conn.execute("PRAGMA wal_checkpoint(TRUNCATE)")
 
 
282
  self._conn.close()
283
  except Exception:
284
  pass
 
897
  with self._reader() as conn:
898
  where = " WHERE is_active=1" if active_only else ""
899
  total = conn.execute(
900
+ f"SELECT COUNT(*) FROM skill_records{where}"
901
+ ).fetchone()[0]
902
 
903
  by_category = {
904
  r["category"]: r["cnt"]
 
915
  ).fetchall()
916
  }
917
  n_analyses = conn.execute(
918
+ "SELECT COUNT(*) FROM execution_analyses"
919
+ ).fetchone()[0]
920
  n_candidates = conn.execute(
921
+ "SELECT COUNT(*) FROM execution_analyses "
922
  "WHERE candidate_for_evolution=1"
923
+ ).fetchone()[0]
924
  agg = conn.execute(
925
  f"""
926
  SELECT SUM(total_selections) AS sel,
 
933
 
934
  # Also report total (including inactive) for context
935
  total_all = conn.execute(
936
+ "SELECT COUNT(*) FROM skill_records"
937
+ ).fetchone()[0]
938
 
939
  return {
940
  "total_skills": total,
openspace/utils/__pycache__/logging.cpython-311.pyc ADDED
Binary file (15.9 kB). View file
 
requirements.txt CHANGED
@@ -12,7 +12,7 @@ colorama>=0.4.6
12
  # Local server dependencies (cross-platform)
13
  flask>=3.1.0
14
  flask-cors>=4.0.0
15
- pyautogui>=0.9.54
16
  pydantic>=2.12.0
17
  requests>=2.32.0
18
  libsql-experimental
 
12
  # Local server dependencies (cross-platform)
13
  flask>=3.1.0
14
  flask-cors>=4.0.0
15
+ # pyautogui>=0.9.54 # Removed for cloud deployment
16
  pydantic>=2.12.0
17
  requests>=2.32.0
18
  libsql-experimental