edgeone 1.5.8 → 1.6.0

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 (47) hide show
  1. package/README.md +26 -26
  2. package/edgeone-bin/edgeone.js +3 -3
  3. package/edgeone-dist/cli.js +86879 -2294
  4. package/edgeone-dist/libs-pages-agent-toolkit/README.md +8 -0
  5. package/edgeone-dist/libs-pages-agent-toolkit/pages_agent_toolkit-0.1.40-py3-none-any.whl +0 -0
  6. package/edgeone-dist/libs-pages-blob-python/README.md +38 -0
  7. package/edgeone-dist/libs-pages-blob-python/pages_blob_python-0.11.0-py3-none-any.whl +0 -0
  8. package/edgeone-dist/pages/dev/runner-worker.js +86519 -2075
  9. package/edgeone-dist/pages/observability-python/__init__.py +32 -0
  10. package/edgeone-dist/pages/observability-python/_compat.py +69 -0
  11. package/edgeone-dist/pages/observability-python/apm/__init__.py +13 -0
  12. package/edgeone-dist/pages/observability-python/apm/config.py +85 -0
  13. package/edgeone-dist/pages/observability-python/apm/llm_semconv.py +53 -0
  14. package/edgeone-dist/pages/observability-python/apm/metrics_bridge.py +226 -0
  15. package/edgeone-dist/pages/observability-python/apm/span_exporter.py +384 -0
  16. package/edgeone-dist/pages/observability-python/bootstrap.py +158 -0
  17. package/edgeone-dist/pages/observability-python/build.py +119 -0
  18. package/edgeone-dist/pages/observability-python/context_patches.py +167 -0
  19. package/edgeone-dist/pages/observability-python/context_propagator.py +78 -0
  20. package/edgeone-dist/pages/observability-python/registry.json +95 -0
  21. package/edgeone-dist/pages/observability-python/registry.py +141 -0
  22. package/edgeone-dist/pages/observability-python/telemetry.py +214 -0
  23. package/edgeone-dist/pages/observability-python/tracer.py +165 -0
  24. package/edgeone-dist/pages/templates/agent-python/__init__.py +11 -0
  25. package/edgeone-dist/pages/templates/agent-python/adapter.py +908 -0
  26. package/edgeone-dist/pages/templates/agent-python/context.py +689 -0
  27. package/edgeone-dist/pages/templates/agent-python/local_blob_store.py +172 -0
  28. package/edgeone-dist/pages/templates/agent-python/memory.py +2301 -0
  29. package/edgeone-dist/pages/templates/agent-python/runtime.py +839 -0
  30. package/edgeone-dist/pages/templates/agent-python/store.py +204 -0
  31. package/edgeone-dist/studio/ui/assets/agent-obs-Dvi4IpEy.js +4 -0
  32. package/edgeone-dist/studio/ui/assets/agent-obs-qDJCE0TQ.css +1 -0
  33. package/edgeone-dist/studio/ui/assets/highlight-ClXAL37H.js +3 -0
  34. package/edgeone-dist/studio/ui/assets/index-Cz5oQnXW.css +1 -0
  35. package/edgeone-dist/studio/ui/assets/index-DD3d108t.js +1 -0
  36. package/edgeone-dist/studio/ui/assets/moment-BYRO94Ou.js +10 -0
  37. package/edgeone-dist/studio/ui/assets/react-dom-ZzBHVjtL.js +24 -0
  38. package/edgeone-dist/studio/ui/assets/react-hnpCyKql.js +17 -0
  39. package/edgeone-dist/studio/ui/assets/tea-CADagUwM.css +1 -0
  40. package/edgeone-dist/studio/ui/assets/tea-Slf_ajmf.js +334 -0
  41. package/edgeone-dist/studio/ui/favicon.ico +0 -0
  42. package/edgeone-dist/studio/ui/index.html +31 -0
  43. package/libs-pages-agent-toolkit/README.md +8 -0
  44. package/libs-pages-agent-toolkit/pages_agent_toolkit-0.1.40-py3-none-any.whl +0 -0
  45. package/libs-pages-blob-python/README.md +38 -0
  46. package/libs-pages-blob-python/pages_blob_python-0.11.0-py3-none-any.whl +0 -0
  47. package/package.json +33 -7
@@ -0,0 +1,172 @@
1
+ # src/pages/builder/templates/agent-python/local_blob_store.py
2
+ """LocalFileBlobStore — 文件系统持久化的 pages-blob 替身,仅用于本地 dev。
3
+
4
+ 触发方式:设置环境变量 PAGES_BLOB_LOCAL_PERSIST=1。
5
+ 数据路径:默认 .edgeone/agent-python/.blob-local/<store-name>/<url_quote(key)>.bin
6
+ (可用 PAGES_BLOB_LOCAL_DIR 覆盖)
7
+
8
+ 接口与 pages_blob.Store 鸭子兼容(只实现 BlobBackedStore 实际用到的 4 个方法:
9
+ get / set / delete / list)。不依赖 pages_blob 包,离线环境也能用。
10
+
11
+ 不支持的能力:TTL 过期清理(由 BlobBackedStore 的 envelope 自己兜底)、
12
+ content-type、cacheControl、CDN 头——本地 dev 场景不需要。
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import asyncio
17
+ import os
18
+ import urllib.parse
19
+ from dataclasses import dataclass, field
20
+ from pathlib import Path
21
+ from typing import List, Optional
22
+
23
+
24
+ @dataclass
25
+ class _BlobInfo:
26
+ """鸭子兼容 pages_blob.types.BlobInfo"""
27
+ key: str
28
+ etag: str = ""
29
+
30
+
31
+ @dataclass
32
+ class _ListResult:
33
+ """鸭子兼容 pages_blob.types.ListResult"""
34
+ blobs: List[_BlobInfo] = field(default_factory=list)
35
+ directories: List[str] = field(default_factory=list)
36
+
37
+
38
+ def _safe_filename(key: str) -> str:
39
+ """key → 文件名。用 URL encode 保留可读性,同时避免路径穿越。
40
+
41
+ 注意不用 hash:debug 时能直接 ls 看到原 key。
42
+ """
43
+ encoded = urllib.parse.quote(key, safe="")
44
+ # 加后缀,方便 rm -rf 清理
45
+ return encoded + ".bin"
46
+
47
+
48
+ def _unsafe_key(filename: str) -> str:
49
+ """反向:文件名 → key"""
50
+ if filename.endswith(".bin"):
51
+ filename = filename[:-4]
52
+ return urllib.parse.unquote(filename)
53
+
54
+
55
+ class LocalFileBlobStore:
56
+ """文件持久化的 pages_blob.Store 替身。
57
+
58
+ 每个 store 对应 base_dir/<store_name>/ 目录,每个 key 对应一个文件。
59
+ 同目录下多 handler 并行写入同一个 key:使用 atomic write(先写 .tmp
60
+ 再 rename),确保读取端永远看到完整内容。
61
+ """
62
+
63
+ def __init__(self, store_name: str, base_dir: Path) -> None:
64
+ self._store_name = store_name
65
+ self._dir = base_dir / store_name
66
+ self._dir.mkdir(parents=True, exist_ok=True)
67
+
68
+ def _path_for(self, key: str) -> Path:
69
+ return self._dir / _safe_filename(key)
70
+
71
+ async def get(self, key: str, *, type: str = "text", **_kwargs) -> Optional[object]:
72
+ path = self._path_for(key)
73
+ if not path.exists():
74
+ return None
75
+ # 用线程池读,避免阻塞 event loop(小文件其实无所谓,保持形状对齐)
76
+ body = await asyncio.to_thread(path.read_bytes)
77
+ if type == "bytes":
78
+ return body
79
+ if type == "json":
80
+ import json
81
+ return json.loads(body.decode("utf-8"))
82
+ # 默认 text
83
+ return body.decode("utf-8")
84
+
85
+ async def set(
86
+ self,
87
+ key: str,
88
+ value,
89
+ *,
90
+ only_if_new: bool = False,
91
+ **_kwargs,
92
+ ) -> None:
93
+ path = self._path_for(key)
94
+ if only_if_new and path.exists():
95
+ # 与线上行为对齐:412 → BlobBackedStore 不直接调这个,
96
+ # 但独立调用方可能走到;直接抛出和 pages_blob 对齐的异常。
97
+ raise _PreconditionFailedError()
98
+
99
+ if isinstance(value, str):
100
+ data = value.encode("utf-8")
101
+ elif isinstance(value, (bytes, bytearray)):
102
+ data = bytes(value)
103
+ else:
104
+ # dict / list 等——直接 json 化
105
+ import json
106
+ data = json.dumps(value, ensure_ascii=False).encode("utf-8")
107
+
108
+ # Atomic write:rename 是 POSIX 原子操作
109
+ tmp = path.with_suffix(path.suffix + ".tmp")
110
+ await asyncio.to_thread(tmp.write_bytes, data)
111
+ await asyncio.to_thread(os.replace, tmp, path)
112
+
113
+ async def set_json(self, key: str, value, *, only_if_new: bool = False, **_kwargs) -> None:
114
+ import json
115
+ await self.set(key, json.dumps(value, ensure_ascii=False), only_if_new=only_if_new)
116
+
117
+ async def delete(self, key: str) -> None:
118
+ path = self._path_for(key)
119
+ try:
120
+ await asyncio.to_thread(path.unlink)
121
+ except FileNotFoundError:
122
+ # 对齐 pages_blob:delete 不存在的 key 是幂等操作
123
+ pass
124
+
125
+ async def list(
126
+ self,
127
+ *,
128
+ prefix: Optional[str] = None,
129
+ directories: bool = False,
130
+ cursor: Optional[str] = None,
131
+ paginate: bool = True,
132
+ **_kwargs,
133
+ ) -> _ListResult:
134
+ entries = await asyncio.to_thread(lambda: list(self._dir.iterdir()))
135
+ keys = []
136
+ for entry in entries:
137
+ if not entry.is_file() or not entry.name.endswith(".bin"):
138
+ continue
139
+ key = _unsafe_key(entry.name)
140
+ if prefix and not key.startswith(prefix):
141
+ continue
142
+ keys.append(key)
143
+ keys.sort()
144
+ return _ListResult(blobs=[_BlobInfo(key=k) for k in keys])
145
+
146
+
147
+ class _PreconditionFailedError(Exception):
148
+ """和 pages_blob.errors.PreconditionFailedError 鸭子对齐"""
149
+ code = "PRECONDITION_FAILED"
150
+
151
+
152
+ def get_local_base_dir() -> Optional[Path]:
153
+ """解析本地持久化根目录;未启用返回 None。
154
+
155
+ - PAGES_BLOB_LOCAL_DIR 指定 → 用它
156
+ - PAGES_BLOB_LOCAL_PERSIST=1 → 用 .edgeone/agent-python/.blob-local
157
+ - 否则 → None
158
+ """
159
+ explicit = os.environ.get("PAGES_BLOB_LOCAL_DIR")
160
+ if explicit:
161
+ return Path(explicit).expanduser().resolve()
162
+
163
+ flag = os.environ.get("PAGES_BLOB_LOCAL_PERSIST", "").strip().lower()
164
+ if flag in ("1", "true", "yes", "on"):
165
+ # adapter.py 同目录下的 .blob-local(main.py 位置就是 OUTPUT_DIR)
166
+ main_py = os.environ.get("_AGENT_PYTHON_MAIN", "")
167
+ if main_py:
168
+ return Path(main_py).parent / ".blob-local"
169
+ # Fallback: cwd 下
170
+ return Path.cwd() / ".blob-local"
171
+
172
+ return None