@cicctencent/agent-server 0.2.97 → 1.0.2
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/dist/cjs/automation/node-task-runtime.d.ts.map +1 -1
- package/dist/cjs/automation/node-task-runtime.js +196 -8
- package/dist/cjs/automation/node-task-runtime.js.map +1 -1
- package/dist/cjs/automation/node-task-validator.d.ts +10 -0
- package/dist/cjs/automation/node-task-validator.d.ts.map +1 -1
- package/dist/cjs/automation/node-task-validator.js +10 -0
- package/dist/cjs/automation/node-task-validator.js.map +1 -1
- package/dist/cjs/automation/python-prelude.d.ts +1 -1
- package/dist/cjs/automation/python-prelude.d.ts.map +1 -1
- package/dist/cjs/automation/python-prelude.js +196 -1
- package/dist/cjs/automation/python-prelude.js.map +1 -1
- package/dist/cjs/automation/python-task-runtime.d.ts.map +1 -1
- package/dist/cjs/automation/python-task-runtime.js +56 -4
- package/dist/cjs/automation/python-task-runtime.js.map +1 -1
- package/dist/cjs/sandbox/sandbox-prelude.d.ts +1 -1
- package/dist/cjs/sandbox/sandbox-prelude.d.ts.map +1 -1
- package/dist/cjs/sandbox/sandbox-prelude.js +55 -0
- package/dist/cjs/sandbox/sandbox-prelude.js.map +1 -1
- package/dist/esm/automation/node-task-runtime.d.ts.map +1 -1
- package/dist/esm/automation/node-task-runtime.js +196 -8
- package/dist/esm/automation/node-task-runtime.js.map +1 -1
- package/dist/esm/automation/node-task-validator.d.ts +10 -0
- package/dist/esm/automation/node-task-validator.d.ts.map +1 -1
- package/dist/esm/automation/node-task-validator.js +10 -0
- package/dist/esm/automation/node-task-validator.js.map +1 -1
- package/dist/esm/automation/python-prelude.d.ts +1 -1
- package/dist/esm/automation/python-prelude.d.ts.map +1 -1
- package/dist/esm/automation/python-prelude.js +196 -1
- package/dist/esm/automation/python-prelude.js.map +1 -1
- package/dist/esm/automation/python-task-runtime.d.ts.map +1 -1
- package/dist/esm/automation/python-task-runtime.js +56 -4
- package/dist/esm/automation/python-task-runtime.js.map +1 -1
- package/dist/esm/sandbox/sandbox-prelude.d.ts +1 -1
- package/dist/esm/sandbox/sandbox-prelude.d.ts.map +1 -1
- package/dist/esm/sandbox/sandbox-prelude.js +55 -0
- package/dist/esm/sandbox/sandbox-prelude.js.map +1 -1
- package/package.json +2 -2
|
@@ -25,12 +25,20 @@ import datetime as _dt
|
|
|
25
25
|
import urllib.request
|
|
26
26
|
import urllib.error
|
|
27
27
|
import urllib.parse
|
|
28
|
+
import threading
|
|
29
|
+
import hashlib
|
|
30
|
+
import uuid as _uuid
|
|
31
|
+
import random as _random
|
|
28
32
|
from typing import Any, Dict, List, Optional
|
|
29
33
|
|
|
30
34
|
# Agent 调用端点(由运行时注入)
|
|
31
35
|
_AGENT_ENDPOINT = "__AGENT_ENDPOINT__"
|
|
32
36
|
_AGENT_CONFIGURED = bool(_AGENT_ENDPOINT)
|
|
33
37
|
|
|
38
|
+
# 内存缓存(带 TTL),用于 sb.cache
|
|
39
|
+
_CACHE_STORE: Dict[str, Any] = {}
|
|
40
|
+
_CACHE_LOCK = threading.Lock()
|
|
41
|
+
|
|
34
42
|
|
|
35
43
|
class _Logger:
|
|
36
44
|
def __init__(self, sink):
|
|
@@ -232,14 +240,85 @@ def parse_date(text, fmt=None):
|
|
|
232
240
|
return _dt.datetime.fromisoformat(text)
|
|
233
241
|
|
|
234
242
|
|
|
235
|
-
def
|
|
243
|
+
def days_ago(n):
|
|
236
244
|
return _dt.datetime.now() - _dt.timedelta(days=n)
|
|
237
245
|
|
|
238
246
|
|
|
247
|
+
# 兼容旧名(文档与示例统一使用 sb.days_ago)
|
|
248
|
+
days_go = days_ago
|
|
249
|
+
|
|
250
|
+
|
|
239
251
|
def days_later(n):
|
|
240
252
|
return _dt.datetime.now() + _dt.timedelta(days=n)
|
|
241
253
|
|
|
242
254
|
|
|
255
|
+
# ===== 工具函数(Util) =====
|
|
256
|
+
|
|
257
|
+
def sleep(seconds):
|
|
258
|
+
"""阻塞当前协程/线程指定秒数。"""
|
|
259
|
+
time.sleep(seconds)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def uuid() -> str:
|
|
263
|
+
"""生成 UUID4 字符串。"""
|
|
264
|
+
return str(_uuid.uuid4())
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def hash(value, algo="sha256") -> str:
|
|
268
|
+
"""计算字符串/对象的哈希(默认 sha256)。"""
|
|
269
|
+
h = hashlib.new(algo)
|
|
270
|
+
h.update(str(value).encode("utf-8"))
|
|
271
|
+
return h.hexdigest()
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def random_int(min_value=0, max_value=100) -> int:
|
|
275
|
+
"""返回 [min_value, max_value] 区间内的随机整数。"""
|
|
276
|
+
return _random.randint(min_value, max_value)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def random_choice(seq):
|
|
280
|
+
"""从序列中随机取一个元素。"""
|
|
281
|
+
return _random.choice(seq)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def random_float(min_value=0.0, max_value=1.0) -> float:
|
|
285
|
+
"""返回 [min_value, max_value] 区间内的随机浮点数。"""
|
|
286
|
+
return _random.uniform(min_value, max_value)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def cache(key: str, ttl: Optional[float] = None):
|
|
290
|
+
"""带 TTL 的内存缓存访问器。
|
|
291
|
+
|
|
292
|
+
用法:
|
|
293
|
+
hits = sb.cache("api:hot", ttl=60).get(lambda: sb.get("/hot").get("data"))
|
|
294
|
+
sb.cache("api:hot").clear() # 清除该 key
|
|
295
|
+
|
|
296
|
+
ttl 为 None 表示永不过期(进程内)。
|
|
297
|
+
返回对象含 get(factory) / clear() 两个方法。
|
|
298
|
+
"""
|
|
299
|
+
class _CacheEntry:
|
|
300
|
+
__slots__ = ("value", "expire_at")
|
|
301
|
+
def __init__(self, value, expire_at):
|
|
302
|
+
self.value = value
|
|
303
|
+
self.expire_at = expire_at
|
|
304
|
+
|
|
305
|
+
def _get(factory):
|
|
306
|
+
now_ts = time.time()
|
|
307
|
+
with _CACHE_LOCK:
|
|
308
|
+
entry = _CACHE_STORE.get(key)
|
|
309
|
+
if entry is not None and (entry.expire_at is None or entry.expire_at > now_ts):
|
|
310
|
+
return entry.value
|
|
311
|
+
value = factory() if callable(factory) else factory
|
|
312
|
+
_CACHE_STORE[key] = _CacheEntry(value, now_ts + ttl if ttl else None)
|
|
313
|
+
return value
|
|
314
|
+
|
|
315
|
+
def _clear():
|
|
316
|
+
with _CACHE_LOCK:
|
|
317
|
+
_CACHE_STORE.pop(key, None)
|
|
318
|
+
|
|
319
|
+
return type("CacheHandle", (), {"get": _get, "clear": _clear})()
|
|
320
|
+
|
|
321
|
+
|
|
243
322
|
# ===== Agent =====
|
|
244
323
|
|
|
245
324
|
class _Agent:
|
|
@@ -252,11 +331,18 @@ class _Agent:
|
|
|
252
331
|
- 流式逐 token 返回,避免非流式 HTTP 长等待超时;
|
|
253
332
|
- 服务端每 3s 发送心跳,保持连接活跃;
|
|
254
333
|
- timeout 为单次读取超时(默认 600s),流式心跳保证不会因空闲而超时。
|
|
334
|
+
|
|
335
|
+
options:
|
|
336
|
+
- timeout : 单次读取超时(秒)
|
|
337
|
+
- contextId : 会话上下文 id(用于多轮记忆)
|
|
338
|
+
- profile : Agent profile(名称或 id)
|
|
339
|
+
- json : True 时把最终文本按 JSON 解析并返回对象(自动去除 \`\`\` 代码围栏)
|
|
255
340
|
"""
|
|
256
341
|
if not _AGENT_CONFIGURED:
|
|
257
342
|
raise RuntimeError("Agent endpoint not configured")
|
|
258
343
|
options = options or {}
|
|
259
344
|
timeout = options.get("timeout", 600)
|
|
345
|
+
json_mode = options.get("json", False)
|
|
260
346
|
payload = {
|
|
261
347
|
"jsonrpc": "2.0",
|
|
262
348
|
"id": options.get("id", 1),
|
|
@@ -320,8 +406,117 @@ class _Agent:
|
|
|
320
406
|
pass
|
|
321
407
|
if task_failed:
|
|
322
408
|
raise RuntimeError("Agent task failed: %s" % (task_error or "unknown"))
|
|
409
|
+
if json_mode:
|
|
410
|
+
return _parse_json_text(final_text)
|
|
323
411
|
return final_text
|
|
324
412
|
|
|
413
|
+
def stream(self, prompt, options=None):
|
|
414
|
+
"""流式调用 Agent,增量 yield 文本片段(delta)。
|
|
415
|
+
|
|
416
|
+
A2A 的 message 事件通常携带「到目前为止的完整文本」,故与前一次文本做前缀
|
|
417
|
+
比较,只输出新增部分(delta),避免重复。
|
|
418
|
+
|
|
419
|
+
options 同 ask(timeout / contextId / profile / json)。
|
|
420
|
+
json=True 时,流式结束后额外 yield 一次解析后的对象(最后一次 yield 为 dict)。
|
|
421
|
+
"""
|
|
422
|
+
if not _AGENT_CONFIGURED:
|
|
423
|
+
raise RuntimeError("Agent endpoint not configured")
|
|
424
|
+
options = options or {}
|
|
425
|
+
timeout = options.get("timeout", 600)
|
|
426
|
+
json_mode = options.get("json", False)
|
|
427
|
+
payload = {
|
|
428
|
+
"jsonrpc": "2.0",
|
|
429
|
+
"id": options.get("id", 1),
|
|
430
|
+
"method": "tasks/sendSubscribe",
|
|
431
|
+
"params": {
|
|
432
|
+
"message": {
|
|
433
|
+
"role": "user",
|
|
434
|
+
"parts": [{"kind": "text", "text": str(prompt)}],
|
|
435
|
+
},
|
|
436
|
+
"contextId": options.get("contextId"),
|
|
437
|
+
"profile": options.get("profile"),
|
|
438
|
+
},
|
|
439
|
+
}
|
|
440
|
+
data = json.dumps(payload).encode("utf-8")
|
|
441
|
+
req = urllib.request.Request(
|
|
442
|
+
_AGENT_ENDPOINT,
|
|
443
|
+
data=data,
|
|
444
|
+
headers={
|
|
445
|
+
"Content-Type": "application/json",
|
|
446
|
+
"Accept": "text/event-stream",
|
|
447
|
+
|
|
448
|
+
"User-Agent": "workai-sandbox/1.0",
|
|
449
|
+
},
|
|
450
|
+
method="POST",
|
|
451
|
+
)
|
|
452
|
+
last = ""
|
|
453
|
+
buf = b""
|
|
454
|
+
try:
|
|
455
|
+
resp = urllib.request.urlopen(req, timeout=timeout)
|
|
456
|
+
except urllib.error.HTTPError as e:
|
|
457
|
+
raise RuntimeError("Agent call failed (HTTP %s)" % e.code)
|
|
458
|
+
try:
|
|
459
|
+
for raw in resp:
|
|
460
|
+
buf += raw
|
|
461
|
+
while b"\n\n" in buf:
|
|
462
|
+
block, buf = buf.split(b"\n\n", 1)
|
|
463
|
+
text = block.decode("utf-8", errors="replace")
|
|
464
|
+
for line in text.splitlines():
|
|
465
|
+
if not line.startswith("data:"):
|
|
466
|
+
continue
|
|
467
|
+
payload_str = line[5:].strip()
|
|
468
|
+
if not payload_str:
|
|
469
|
+
continue
|
|
470
|
+
try:
|
|
471
|
+
msg = json.loads(payload_str)
|
|
472
|
+
except Exception:
|
|
473
|
+
continue
|
|
474
|
+
result = msg.get("result") or {}
|
|
475
|
+
etype = result.get("type")
|
|
476
|
+
edata = result.get("data")
|
|
477
|
+
if etype == "error":
|
|
478
|
+
err_msg = edata.get("message") if isinstance(edata, dict) else str(edata)
|
|
479
|
+
raise RuntimeError("Agent error: %s" % err_msg)
|
|
480
|
+
t = ""
|
|
481
|
+
if etype == "message" and isinstance(edata, dict):
|
|
482
|
+
for p in (edata.get("parts") or []):
|
|
483
|
+
if (p.get("kind") == "text" or p.get("type") == "text") and p.get("text"):
|
|
484
|
+
t = p["text"]
|
|
485
|
+
elif etype == "task" and isinstance(edata, dict) and edata.get("state") == "failed":
|
|
486
|
+
raise RuntimeError("Agent task failed: %s" % (edata.get("error") or "unknown"))
|
|
487
|
+
if not t:
|
|
488
|
+
continue
|
|
489
|
+
# 前缀差量:只输出新增内容
|
|
490
|
+
if t.startswith(last):
|
|
491
|
+
delta = t[len(last):]
|
|
492
|
+
if delta:
|
|
493
|
+
yield delta
|
|
494
|
+
else:
|
|
495
|
+
yield t
|
|
496
|
+
last = t
|
|
497
|
+
finally:
|
|
498
|
+
try:
|
|
499
|
+
resp.close()
|
|
500
|
+
except Exception:
|
|
501
|
+
pass
|
|
502
|
+
if json_mode and last:
|
|
503
|
+
yield _parse_json_text(last)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def _parse_json_text(text):
|
|
507
|
+
"""把 Agent 返回文本按 JSON 解析(自动去除 \`\`\`json 代码围栏)。"""
|
|
508
|
+
s = (text or "").strip()
|
|
509
|
+
if s.startswith("\`\`\`"):
|
|
510
|
+
# 去除首行围栏(\`\`\`json / \`\`\`)
|
|
511
|
+
s = s.split("\n", 1)[1] if "\n" in s else s[3:]
|
|
512
|
+
if s.endswith("\`\`\`"):
|
|
513
|
+
s = s[:-3]
|
|
514
|
+
s = s.strip()
|
|
515
|
+
try:
|
|
516
|
+
return json.loads(s)
|
|
517
|
+
except Exception:
|
|
518
|
+
raise RuntimeError("Agent 返回内容不是合法 JSON: %s" % s[:200])
|
|
519
|
+
|
|
325
520
|
|
|
326
521
|
agent = _Agent()
|
|
327
522
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"python-prelude.js","sourceRoot":"","sources":["../../../src/automation/python-prelude.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,gDAAgD;AAChD,+DAA+D;AAC/D,kDAAkD;AAClD,kEAAkE;AAClE,6CAA6C;AAC7C,+DAA+D;AAE/D;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG
|
|
1
|
+
{"version":3,"file":"python-prelude.js","sourceRoot":"","sources":["../../../src/automation/python-prelude.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,gDAAgD;AAChD,+DAA+D;AAC/D,kDAAkD;AAClD,kEAAkE;AAClE,6CAA6C;AAC7C,+DAA+D;AAE/D;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8fpC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"python-task-runtime.d.ts","sourceRoot":"","sources":["../../../src/automation/python-task-runtime.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,6BAA6B,EAC7B,sBAAsB,EACtB,wBAAwB,EACxB,yBAAyB,EAC1B,MAAM,yBAAyB,CAAC;AAKjC,MAAM,WAAW,8BAA8B;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAYD,qBAAa,6BAA6B;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,GAAE,8BAAmC;IAIlD,OAAO,CACX,WAAW,EAAE,yBAAyB,EACtC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,6BAA6B,CAAC;IAqCzC;;;OAGG;IACG,aAAa,CACjB,WAAW,EAAE,yBAAyB,EACtC,OAAO,EAAE,wBAAwB,EACjC,SAAS,GAAE;QACT,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;QAChD,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QAClC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;QACnC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,6BAA6B,KAAK,IAAI,CAAC;QAC3D,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;KAChC,GACL,OAAO,CAAC,6BAA6B,CAAC;IAoDzC,+CAA+C;IAC/C,OAAO,CAAC,cAAc;IA8BtB,2CAA2C;IAC3C,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,oBAAoB;
|
|
1
|
+
{"version":3,"file":"python-task-runtime.d.ts","sourceRoot":"","sources":["../../../src/automation/python-task-runtime.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,6BAA6B,EAC7B,sBAAsB,EACtB,wBAAwB,EACxB,yBAAyB,EAC1B,MAAM,yBAAyB,CAAC;AAKjC,MAAM,WAAW,8BAA8B;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAYD,qBAAa,6BAA6B;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,GAAE,8BAAmC;IAIlD,OAAO,CACX,WAAW,EAAE,yBAAyB,EACtC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,6BAA6B,CAAC;IAqCzC;;;OAGG;IACG,aAAa,CACjB,WAAW,EAAE,yBAAyB,EACtC,OAAO,EAAE,wBAAwB,EACjC,SAAS,GAAE;QACT,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;QAChD,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QAClC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;QACnC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,6BAA6B,KAAK,IAAI,CAAC;QAC3D,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;KAChC,GACL,OAAO,CAAC,6BAA6B,CAAC;IAoDzC,+CAA+C;IAC/C,OAAO,CAAC,cAAc;IA8BtB,2CAA2C;IAC3C,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,oBAAoB;IA8I5B,OAAO,CAAC,kBAAkB;IAkB1B,OAAO,CAAC,iBAAiB;CAU1B;AAID,wBAAgB,gCAAgC,IAAI,6BAA6B,CAGhF"}
|
|
@@ -188,24 +188,64 @@ for __file, __content in (__task.get("sourceFiles") or {}).items():
|
|
|
188
188
|
with open(__target, "w", encoding="utf-8") as __f:
|
|
189
189
|
__f.write(__content)
|
|
190
190
|
|
|
191
|
+
# 产物大小上限(单文件默认 5MB,避免超大产物撑爆存储/响应)
|
|
192
|
+
ARTIFACT_MAX_BYTES = 5 * 1024 * 1024
|
|
193
|
+
|
|
194
|
+
def __artifact_guard(content):
|
|
195
|
+
if isinstance(content, (bytes, bytearray)):
|
|
196
|
+
size = len(content)
|
|
197
|
+
else:
|
|
198
|
+
size = len(str(content).encode("utf-8"))
|
|
199
|
+
if size > ARTIFACT_MAX_BYTES:
|
|
200
|
+
raise RuntimeError("产物体积超限(最大 %d 字节): 实际 %d" % (ARTIFACT_MAX_BYTES, size))
|
|
201
|
+
return content
|
|
202
|
+
|
|
191
203
|
# 构造 context(与 Node 端结构对齐)
|
|
204
|
+
# secrets:仅暴露白名单环境变量(与 env 同源),供任务安全读取凭证,避免误用 os.environ 全量泄露。
|
|
192
205
|
__context = {
|
|
193
206
|
"taskId": __data["taskId"],
|
|
194
207
|
"runId": __data["runId"],
|
|
195
208
|
"inputs": __data["inputs"],
|
|
196
209
|
"env": __data["env"],
|
|
210
|
+
"secrets": __data["env"],
|
|
197
211
|
"workspace": __data["workspace"],
|
|
198
212
|
"logger": sb._Logger(__log),
|
|
199
213
|
"artifacts": type("A", (), {
|
|
200
|
-
"writeText": staticmethod(lambda f, c: sb.write_text(f,
|
|
201
|
-
"write_text": staticmethod(lambda f, c: sb.write_text(f,
|
|
214
|
+
"writeText": staticmethod(lambda f, c: sb.write_text(f, __artifact_guard(c))),
|
|
215
|
+
"write_text": staticmethod(lambda f, c: sb.write_text(f, __artifact_guard(c))),
|
|
202
216
|
"writeJson": staticmethod(lambda f, d: sb.write_json(f, d)),
|
|
203
217
|
"write_json": staticmethod(lambda f, d: sb.write_json(f, d)),
|
|
218
|
+
"readText": staticmethod(lambda f: sb.read_text(f)),
|
|
219
|
+
"read_text": staticmethod(lambda f: sb.read_text(f)),
|
|
220
|
+
"readJson": staticmethod(lambda f: sb.read_json(f)),
|
|
221
|
+
"read_json": staticmethod(lambda f: sb.read_json(f)),
|
|
204
222
|
})(),
|
|
205
223
|
"agent": sb.agent,
|
|
206
224
|
}
|
|
207
225
|
|
|
226
|
+
def __validate_inputs(schema, inputs):
|
|
227
|
+
"""运行前校验 inputsSchema(JSON Schema 子集:properties / required / type)。"""
|
|
228
|
+
if not schema or not isinstance(schema, dict):
|
|
229
|
+
return
|
|
230
|
+
props = schema.get("properties") or {}
|
|
231
|
+
required = schema.get("required") or []
|
|
232
|
+
if not isinstance(inputs, dict):
|
|
233
|
+
raise RuntimeError("inputs 必须是对象,但收到 %s" % type(inputs).__name__)
|
|
234
|
+
for key in required:
|
|
235
|
+
if key not in inputs:
|
|
236
|
+
raise RuntimeError("缺少必填输入项: %s" % key)
|
|
237
|
+
__type_map = {"string": str, "number": (int, float), "integer": int,
|
|
238
|
+
"boolean": bool, "array": list, "object": dict}
|
|
239
|
+
for key, val in inputs.items():
|
|
240
|
+
if key in props and isinstance(props[key], dict):
|
|
241
|
+
expected = props[key].get("type")
|
|
242
|
+
if expected and expected in __type_map and not isinstance(val, __type_map[expected]):
|
|
243
|
+
raise RuntimeError("输入项 %s 类型错误:期望 %s,实际 %s" % (key, expected, type(val).__name__))
|
|
244
|
+
|
|
208
245
|
def __run():
|
|
246
|
+
# 运行前校验输入 Schema(顶层 inputsSchema 或 metadata.inputsSchema)
|
|
247
|
+
__schema = __task.get("inputsSchema") or (__task.get("metadata") or {}).get("inputsSchema")
|
|
248
|
+
__validate_inputs(__schema, __data["inputs"])
|
|
209
249
|
__entry = __task["entry"]
|
|
210
250
|
__mod_name = os.path.splitext(__entry)[0].replace(os.sep, ".")
|
|
211
251
|
import importlib
|
|
@@ -214,13 +254,25 @@ def __run():
|
|
|
214
254
|
raise RuntimeError("Python automation entry must define run(context)")
|
|
215
255
|
return __mod.run(__context)
|
|
216
256
|
|
|
257
|
+
__start = time.time()
|
|
217
258
|
try:
|
|
218
259
|
__result = __run()
|
|
260
|
+
__duration_ms = int((time.time() - __start) * 1000)
|
|
261
|
+
__log("info", "task finished", {"durationMs": __duration_ms})
|
|
219
262
|
print("__NODE_AUTOMATION_RESULT__" + json.dumps({"result": __result}, ensure_ascii=False, default=str))
|
|
220
263
|
except Exception as e:
|
|
221
|
-
|
|
264
|
+
__duration_ms = int((time.time() - __start) * 1000)
|
|
265
|
+
__log("error", str(e), {"durationMs": __duration_ms})
|
|
222
266
|
traceback.print_exc()
|
|
223
|
-
|
|
267
|
+
# 结构化错误:type / code / retryable 供 UI 友好展示与重试策略判断
|
|
268
|
+
__err = {
|
|
269
|
+
"message": str(e),
|
|
270
|
+
"type": type(e).__name__,
|
|
271
|
+
"code": "TASK_EXECUTION_FAILED",
|
|
272
|
+
"retryable": isinstance(e, (RuntimeError, TimeoutError, ConnectionError, OSError)),
|
|
273
|
+
"durationMs": __duration_ms,
|
|
274
|
+
}
|
|
275
|
+
print("__NODE_AUTOMATION_ERROR__" + json.dumps(__err, ensure_ascii=False, default=str))
|
|
224
276
|
sys.exit(1)
|
|
225
277
|
`;
|
|
226
278
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"python-task-runtime.js","sourceRoot":"","sources":["../../../src/automation/python-task-runtime.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"python-task-runtime.js","sourceRoot":"","sources":["../../../src/automation/python-task-runtime.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+CAA+C;AAC/C,+DAA+D;AAC/D,4DAA4D;AAC5D,6CAA6C;AAC7C,+DAA+D;AAE/D,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAOlC,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAgB5D,MAAM,OAAO,6BAA6B;IACvB,OAAO,CAAS;IAEjC,YAAY,UAA0C,EAAE;QACtD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,kBAAkB,CAAC,sBAAsB,CAAC,CAAC;IAC/E,CAAC;IAED,KAAK,CAAC,OAAO,CACX,WAAsC,EACtC,OAAiC;QAEjC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QAC1C,MAAM,SAAS,GAAG,cAAc,OAAO,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;QAC1D,MAAM,OAAO,GAAG,IAAI,kBAAkB,CAAC,SAAS,EAAE;YAChD,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC;YAC3C,kBAAkB,EAAE,IAAI;SACzB,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;QAErB,wCAAwC;QACxC,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;QAC9E,MAAM,aAAa,GAAG,qBAAqB,CAAC,OAAO,CAAC,qBAAqB,EAAE,aAAa,CAAC,CAAC;QAC1F,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAC1D,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;QAE5E,MAAM,IAAI,GAA6B,EAAE,CAAC;QAE1C,MAAM,OAAO,GAAmB;YAC9B,WAAW;YACX,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;YAC5B,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC;YAClD,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,KAAK;YACL,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,aAAa;SACd,CAAC;QAEF,MAAM,MAAM,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAClC,MAAM,EACN,QAAQ,EACR,OAAO,CAAC,SAAS,IAAI,WAAW,CAAC,aAAa,EAAE,SAAS,IAAI,MAAM,CACpE,CAAC;QAEF,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IACxD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,aAAa,CACjB,WAAsC,EACtC,OAAiC,EACjC,YAMI,EAAE;QAEN,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QAC1C,MAAM,SAAS,GAAG,cAAc,OAAO,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;QAC1D,MAAM,OAAO,GAAG,IAAI,kBAAkB,CAAC,SAAS,EAAE;YAChD,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC;YAC3C,kBAAkB,EAAE,IAAI;SACzB,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;QAErB,uDAAuD;QACvD,gDAAgD;QAChD,sDAAsD;QACtD,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;QAC9E,MAAM,aAAa,GAAG,qBAAqB,CAAC,OAAO,CAAC,qBAAqB,EAAE,aAAa,CAAC,CAAC;QAC1F,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAC1D,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;QAE5E,MAAM,IAAI,GAA6B,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAmB;YAC9B,WAAW;YACX,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;YAC5B,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC;YAClD,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,KAAK;YACL,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,aAAa;SACd,CAAC;QAEF,MAAM,MAAM,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,aAAa,CAC3C,MAAM,EACN,QAAQ,EACR,OAAO,CAAC,SAAS,IAAI,WAAW,CAAC,aAAa,EAAE,SAAS,IAAI,MAAM,EACnE;YACE,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE;gBACrB,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC;gBAC3B,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC/B,IAAI,CAAC;wBACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAA2B,CAAC;wBAChF,SAAS,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;oBAC3B,CAAC;oBAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;YACD,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC;SACtD,CACF,CAAC;QAEF,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;QACjE,SAAS,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC;QAC7B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,+CAA+C;IACvC,cAAc,CACpB,MAA+F,EAC/F,IAA8B,EAC9B,WAAsC;QAEtC,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACtD,8CAA8C;QAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxD,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;QACjD,MAAM,MAAM,GAAG,SAAS;YACtB,CAAC,CAAC,SAAS,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YACjE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;QAClB,uBAAuB;QACvB,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzD,yDAAyD;QACzD,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1E,IAAI,WAAW,CAAC,KAAK;YAAE,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC5D,MAAM,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACtE,OAAO;YACL,MAAM,EAAE,WAAW;YACnB,MAAM;YACN,QAAQ;YACR,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,KAAK,EAAE,aAAa;YACpB,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;YAC/B,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,SAAS,EAAE,aAAa;SACzB,CAAC;IACJ,CAAC;IAED,2CAA2C;IACnC,gBAAgB,CAAC,MAAc;QACrC,OAAO,MAAM;aACV,KAAK,CAAC,IAAI,CAAC;aACX,MAAM,CAAC,IAAI,CAAC,EAAE,CACb,CAAC,IAAI,CAAC,UAAU,CAAC,4BAA4B,CAAC;YAC9C,CAAC,IAAI,CAAC,UAAU,CAAC,2BAA2B,CAAC;YAC7C,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAC5B;aACA,IAAI,CAAC,IAAI,CAAC;aACV,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC;aAC1B,IAAI,EAAE,CAAC;IACZ,CAAC;IAEO,QAAQ,CACd,WAAsC,EACtC,GAAuC;QAEvC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,aAAa,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;QAClE,MAAM,OAAO,GAA2B,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,IAAI,GAAG,CAAC,GAAG,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAE,CAAC;QACzC,CAAC;QACD,OAAO,CAAC,gBAAgB,GAAG,GAAG,CAAC;QAC/B,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,oBAAoB,CAAC,OAAuB;QAClD,qDAAqD;QACrD,8DAA8D;QAC9D,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC;YAChD,IAAI,EAAE,OAAO,CAAC,WAAW;YACzB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI;YAC5B,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI;YACpC,aAAa,EAAE,OAAO,CAAC,aAAa;SACrC,CAAC,CAAC,CAAC;QAEJ,OAAO;;;;;;;;;;;;;;;;;;;sBAmBW,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2GhC,CAAC;IACA,CAAC;IAEO,kBAAkB,CAAC,MAAc;QACvC,wCAAwC;QACxC,MAAM,IAAI,GAA6B,EAAE,CAAC;QAC1C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC/B,IAAI,CAAC;oBAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;YACpF,CAAC;QACH,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,4BAA4B,CAAC,CAAC,CAAC;QAClG,IAAI,CAAC,UAAU;YAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC,CAAC;YAChF,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,IAAI,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;IAEO,iBAAiB,CAAC,MAAc;QACtC,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,2BAA2B,CAAC,CAAC,CAAC;QAChG,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAC,CAAC;YAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,oCAAoC,EAAE,CAAC;QAC7E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,OAAO,EAAE,oCAAoC,EAAE,CAAC;QAC3D,CAAC;IACH,CAAC;CACF;AAED,IAAI,aAAa,GAAyC,IAAI,CAAC;AAE/D,MAAM,UAAU,gCAAgC;IAC9C,IAAI,CAAC,aAAa;QAAE,aAAa,GAAG,IAAI,6BAA6B,EAAE,CAAC;IACxE,OAAO,aAAa,CAAC;AACvB,CAAC"}
|
|
@@ -50,5 +50,5 @@ export declare function clearSandboxPreludeExtensions(): void;
|
|
|
50
50
|
* 在沙箱初始化时写入 _scripts/_prelude.cjs
|
|
51
51
|
* 通过 runScript Node.js preamble 自动 require 为 global.sb
|
|
52
52
|
*/
|
|
53
|
-
export declare const SANDBOX_PRELUDE_SOURCE = "'use strict';\n// ============================================================\n// Sandbox Prelude - Auto-loaded as global `sb` in Node.js scripts\n// Common utilities so you only write business logic.\n// Loads extensions from _prelude_ext/ at require time.\n// ============================================================\n\nconst fs = require('fs');\nconst path = require('path');\nconst https = require('https');\nconst http = require('http');\nconst { URL } = require('url');\n\n// ===== Helpers =====\n\n// Safe JSON stringify (handles circular references)\nfunction _safeStringify(v) {\n try { return JSON.stringify(v); } catch (e) { return String(v); }\n}\n\n// ===== HTTP =====\n\n/**\n * fetch - HTTP request with timeout, JSON parsing and redirect following\n * @param {string} url - Request URL\n * @param {object} options - { method, headers, body, json, params, timeout, maxRedirects }\n * @returns {Promise<{status, headers, data, text}>}\n */\nasync function fetch(url, options = {}, _depth = 0) {\n const {\n method = 'GET',\n headers = {},\n body,\n json,\n timeout = 30000,\n params,\n maxRedirects = 3,\n } = options;\n let targetUrl = url;\n if (params) {\n const u = new URL(targetUrl);\n for (const [k, v] of Object.entries(params)) u.searchParams.set(k, String(v));\n targetUrl = u.toString();\n }\n const u = new URL(targetUrl);\n const lib = u.protocol === 'https:' ? https : http;\n const reqHeaders = { 'User-Agent': 'workai-sandbox/1.0', ...headers };\n let reqBody = body;\n if (json !== undefined) {\n reqBody = JSON.stringify(json);\n reqHeaders['Content-Type'] = reqHeaders['Content-Type'] || 'application/json';\n }\n return new Promise((resolve, reject) => {\n const req = lib.request(u, { method, headers: reqHeaders, timeout }, (res) => {\n const status = res.statusCode;\n // Follow redirects (301/302/303/307/308)\n if (\n (status === 301 || status === 302 || status === 303 || status === 307 || status === 308) &&\n res.headers.location &&\n _depth < maxRedirects\n ) {\n res.resume(); // discard response body\n const nextUrl = new URL(res.headers.location, targetUrl).toString();\n const nextMethod = status === 303 ? 'GET' : method;\n const nextBody = status === 303 ? undefined : body;\n const nextJson = status === 303 ? undefined : json;\n resolve(fetch(nextUrl, { ...options, method: nextMethod, body: nextBody, json: nextJson }, _depth + 1));\n return;\n }\n const chunks = [];\n res.on('data', (c) => chunks.push(c));\n res.on('end', () => {\n const buf = Buffer.concat(chunks);\n const text = buf.toString('utf-8');\n let data = text;\n const ct = (res.headers['content-type'] || '').toLowerCase();\n if (ct.includes('application/json') || ct.includes('+json')) {\n try { data = JSON.parse(text); } catch (e) { /* keep text */ }\n }\n resolve({ status: status, headers: res.headers, data, text });\n });\n });\n req.on('error', reject);\n req.on('timeout', () => { req.destroy(new Error('Request timeout after ' + timeout + 'ms')); });\n if (reqBody) req.write(reqBody);\n req.end();\n });\n}\n\n/** GET request shorthand */\nasync function get(url, options = {}) {\n return fetch(url, { ...options, method: 'GET' });\n}\n\n/** POST request shorthand (auto-JSON for object bodies) */\nasync function post(url, body, options = {}) {\n const isJson = typeof body === 'object' && body !== null && !Buffer.isBuffer(body);\n return fetch(url, {\n ...options,\n method: 'POST',\n json: isJson ? body : undefined,\n body: isJson ? undefined : body,\n });\n}\n\n/** PUT request shorthand (auto-JSON for object bodies) */\nasync function put(url, body, options = {}) {\n const isJson = typeof body === 'object' && body !== null && !Buffer.isBuffer(body);\n return fetch(url, {\n ...options,\n method: 'PUT',\n json: isJson ? body : undefined,\n body: isJson ? undefined : body,\n });\n}\n\n/** HEAD request shorthand */\nasync function head(url, options = {}) {\n return fetch(url, { ...options, method: 'HEAD' });\n}\n\n/** DELETE request shorthand */\nasync function del(url, options = {}) {\n return fetch(url, { ...options, method: 'DELETE' });\n}\n\n// ===== File I/O =====\n\nfunction readJson(file) {\n const content = fs.readFileSync(file, 'utf-8');\n return JSON.parse(content);\n}\n\nfunction writeJson(file, data, indent = 2) {\n const dir = path.dirname(file);\n if (dir && dir !== '.') try { fs.mkdirSync(dir, { recursive: true }); } catch (e) {}\n fs.writeFileSync(file, JSON.stringify(data, null, indent), 'utf-8');\n}\n\nfunction readText(file, encoding = 'utf-8') {\n return fs.readFileSync(file, encoding);\n}\n\nfunction writeText(file, content) {\n const dir = path.dirname(file);\n if (dir && dir !== '.') try { fs.mkdirSync(dir, { recursive: true }); } catch (e) {}\n fs.writeFileSync(file, content, 'utf-8');\n}\n\nfunction appendText(file, content) {\n const dir = path.dirname(file);\n if (dir && dir !== '.') try { fs.mkdirSync(dir, { recursive: true }); } catch (e) {}\n fs.appendFileSync(file, content, 'utf-8');\n}\n\nfunction exists(p) {\n try { return fs.existsSync(p); } catch (e) { return false; }\n}\n\nfunction listDir(dir) {\n return fs.readdirSync(dir).map(function (name) {\n let stat;\n try { stat = fs.statSync(path.join(dir, name)); } catch (e) { return name; }\n return stat.isDirectory() ? name + '/' : name;\n });\n}\n\n/**\n * readCsv - parse CSV file into array of objects\n * @param {string} file - CSV file path\n * @param {object} opts - { delimiter, headers }\n * headers: \u81EA\u5B9A\u4E49\u8868\u5934\u6570\u7EC4\uFF1B\u82E5\u63D0\u4F9B\u5219\u9996\u884C\u88AB\u89C6\u4E3A\u6570\u636E\u884C\uFF0C\u5426\u5219\u9996\u884C\u4F5C\u4E3A\u8868\u5934\n * @returns {Array<object>}\n */\nfunction readCsv(file, opts = {}) {\n const delimiter = opts.delimiter || ',';\n const content = fs.readFileSync(file, 'utf-8');\n const lines = content.split(/\\r?\\n/).filter(function (l) { return l.trim(); });\n if (lines.length === 0) return [];\n const parseLine = function (line) {\n const result = [];\n let cur = '';\n let inQuote = false;\n for (let i = 0; i < line.length; i++) {\n const c = line[i];\n if (c === '\"') {\n if (inQuote && line[i + 1] === '\"') { cur += '\"'; i++; }\n else inQuote = !inQuote;\n } else if (c === delimiter && !inQuote) {\n result.push(cur); cur = '';\n } else {\n cur += c;\n }\n }\n result.push(cur);\n return result.map(function (s) { return s.trim(); });\n };\n const headers = opts.headers || parseLine(lines[0]);\n const startIdx = opts.headers ? 0 : 1;\n const rows = [];\n for (let i = startIdx; i < lines.length; i++) {\n const values = parseLine(lines[i]);\n const row = {};\n headers.forEach(function (h, j) { row[h] = values[j] || ''; });\n rows.push(row);\n }\n return rows;\n}\n\n/**\n * writeCsv - write array of objects to CSV file\n * @param {string} file - Output CSV file path\n * @param {Array<object>} rows - Data rows\n * @param {object} opts - { headers, delimiter }\n */\nfunction writeCsv(file, rows, opts = {}) {\n if (!rows || rows.length === 0) {\n writeText(file, '');\n return;\n }\n const delimiter = opts.delimiter || ',';\n const headers = opts.headers || Object.keys(rows[0]);\n const escapeCell = function (val) {\n const s = String(val == null ? '' : val);\n if (s.includes(delimiter) || s.includes('\"') || s.includes('\\n')) {\n return '\"' + s.replace(/\"/g, '\"\"') + '\"';\n }\n return s;\n };\n const lines = [headers.map(escapeCell).join(delimiter)];\n for (const row of rows) {\n lines.push(headers.map(function (h) { return escapeCell(row[h]); }).join(delimiter));\n }\n writeText(file, lines.join('\\n'));\n}\n\n// ===== Data Utilities =====\n\n/** Calculate statistics: sum, avg, min, max, count, std */\nfunction stats(arr) {\n const nums = arr.map(Number).filter(function (n) { return !isNaN(n); });\n if (nums.length === 0) return { count: 0, sum: 0, avg: 0, min: 0, max: 0, std: 0 };\n const sumVal = nums.reduce(function (a, b) { return a + b; }, 0);\n const avgVal = sumVal / nums.length;\n const min = Math.min.apply(null, nums);\n const max = Math.max.apply(null, nums);\n const variance = nums.reduce(function (acc, n) { return acc + Math.pow(n - avgVal, 2); }, 0) / nums.length;\n return { count: nums.length, sum: sumVal, avg: avgVal, min: min, max: max, std: Math.sqrt(variance) };\n}\n\nfunction sum(arr) {\n return arr.reduce(function (a, b) { return a + (Number(b) || 0); }, 0);\n}\n\nfunction avg(arr) {\n if (arr.length === 0) return 0;\n return sum(arr) / arr.length;\n}\n\nfunction groupBy(arr, keyFn) {\n const result = {};\n for (const item of arr) {\n const key = typeof keyFn === 'function' ? keyFn(item) : item[keyFn];\n if (!result[key]) result[key] = [];\n result[key].push(item);\n }\n return result;\n}\n\nfunction sortBy(arr, keyFn, desc) {\n const fn = typeof keyFn === 'function' ? keyFn : function (x) { return x[keyFn]; };\n return arr.slice().sort(function (a, b) {\n const va = fn(a);\n const vb = fn(b);\n if (va < vb) return desc ? 1 : -1;\n if (va > vb) return desc ? -1 : 1;\n return 0;\n });\n}\n\nfunction unique(arr, keyFn) {\n if (!keyFn) {\n return arr.filter(function (v, i, self) { return self.indexOf(v) === i; });\n }\n const fn = typeof keyFn === 'function' ? keyFn : function (x) { return x[keyFn]; };\n const seen = new Set();\n return arr.filter(function (x) {\n const k = fn(x);\n if (seen.has(k)) return false;\n seen.add(k);\n return true;\n });\n}\n\nfunction chunk(arr, size) {\n size = Math.max(1, size || 1);\n const result = [];\n for (let i = 0; i < arr.length; i += size) {\n result.push(arr.slice(i, i + size));\n }\n return result;\n}\n\nfunction range(start, end, step) {\n step = step || 1;\n if (end === undefined) { end = start; start = 0; }\n const result = [];\n if (step > 0) {\n for (let i = start; i < end; i += step) result.push(i);\n } else {\n for (let i = start; i > end; i += step) result.push(i);\n }\n return result;\n}\n\nfunction pick(obj, keys) {\n const result = {};\n for (const k of keys) {\n if (k in obj) result[k] = obj[k];\n }\n return result;\n}\n\nfunction omit(obj, keys) {\n const keySet = new Set(keys);\n const result = {};\n for (const k of Object.keys(obj)) {\n if (!keySet.has(k)) result[k] = obj[k];\n }\n return result;\n}\n\nfunction flatten(arr) {\n return arr.reduce(function (flat, sub) { return flat.concat(Array.isArray(sub) ? sub : [sub]); }, []);\n}\n\n// ===== Date Utilities =====\n\nfunction formatDate(date, format) {\n const d = date instanceof Date ? date : new Date(date);\n if (isNaN(d.getTime())) return 'Invalid Date';\n const pad = function (n) { return n < 10 ? '0' + n : '' + n; };\n const map = {\n YYYY: d.getFullYear(),\n MM: pad(d.getMonth() + 1),\n DD: pad(d.getDate()),\n HH: pad(d.getHours()),\n mm: pad(d.getMinutes()),\n ss: pad(d.getSeconds()),\n };\n let result = format || 'YYYY-MM-DD HH:mm:ss';\n for (const k in map) {\n result = result.replace(k, map[k]);\n }\n return result;\n}\n\nfunction parseDate(str) {\n if (typeof str === 'number') return new Date(str);\n const d = new Date(str);\n if (!isNaN(d.getTime())) return d;\n const m = String(str).match(/^(\\d{4})[-\\/](\\d{1,2})[-\\/](\\d{1,2})/);\n if (m) return new Date(parseInt(m[1], 10), parseInt(m[2], 10) - 1, parseInt(m[3], 10));\n return null;\n}\n\nfunction now() {\n return new Date().toISOString();\n}\n\nfunction daysAgo(n) {\n const d = new Date();\n d.setDate(d.getDate() - n);\n return d;\n}\n\nfunction daysLater(n) {\n const d = new Date();\n d.setDate(d.getDate() + n);\n return d;\n}\n\nfunction dateDiff(d1, d2, unit) {\n const date1 = d1 instanceof Date ? d1 : new Date(d1);\n const date2 = d2 instanceof Date ? d2 : new Date(d2);\n const diffMs = Math.abs(date2.getTime() - date1.getTime());\n unit = unit || 'days';\n switch (unit) {\n case 'seconds': return Math.floor(diffMs / 1000);\n case 'minutes': return Math.floor(diffMs / 60000);\n case 'hours': return Math.floor(diffMs / 3600000);\n case 'days': return Math.floor(diffMs / 86400000);\n default: return diffMs;\n }\n}\n\n// ===== String Utilities =====\n\n/**\n * template - Simple template interpolation\n * @param {string} str - Template string with {variable} or {a.b.c} placeholders\n * @param {object} data - Data object\n * @returns {string}\n */\nfunction template(str, data) {\n return str.replace(/\\{([^}]+)\\}/g, function (match, key) {\n const value = key.trim().split('.').reduce(function (obj, k) {\n return obj && obj[k] !== undefined ? obj[k] : undefined;\n }, data);\n return value !== undefined && value !== null ? String(value) : '';\n });\n}\n\nfunction slugify(str) {\n return String(str)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\nfunction truncate(str, len, suffix) {\n if (String(str).length <= len) return String(str);\n return String(str).substring(0, len) + (suffix || '...');\n}\n\nfunction pad(str, len, char, right) {\n str = String(str);\n char = char || ' ';\n const diff = len - str.length;\n if (diff <= 0) return str;\n const padding = char.repeat(diff);\n return right ? str + padding : padding + str;\n}\n\n// ===== Table Generators =====\n\n/**\n * markdownTable - Generate markdown table from array of objects\n * @param {Array<object>} rows - Data rows\n * @param {Array<string>} headers - Optional custom headers (defaults to Object.keys of first row)\n * @returns {string} Markdown table string\n */\nfunction markdownTable(rows, headers) {\n if (!rows || rows.length === 0) return '';\n headers = headers || Object.keys(rows[0]);\n const lines = [];\n lines.push('| ' + headers.join(' | ') + ' |');\n lines.push('| ' + headers.map(function () { return '---'; }).join(' | ') + ' |');\n for (const row of rows) {\n lines.push('| ' + headers.map(function (h) {\n return String(row[h] == null ? '' : row[h]).replace(/\\|/g, '\\\\|');\n }).join(' | ') + ' |');\n }\n return lines.join('\\n');\n}\n\n/**\n * htmlTable - Generate HTML table from array of objects\n * @param {Array<object>} rows - Data rows\n * @param {object} opts - { headers, tableClass }\n * @returns {string} HTML table string\n */\nfunction htmlTable(rows, opts = {}) {\n if (!rows || rows.length === 0) return '<table><tbody></tbody></table>';\n const headers = opts.headers || Object.keys(rows[0]);\n const tableClass = opts.tableClass ? ' class=\"' + opts.tableClass + '\"' : '';\n const parts = ['<table' + tableClass + '>'];\n parts.push('<thead><tr>');\n for (const h of headers) {\n parts.push('<th>' + h + '</th>');\n }\n parts.push('</tr></thead><tbody>');\n for (const row of rows) {\n parts.push('<tr>');\n for (const h of headers) {\n parts.push('<td>' + String(row[h] == null ? '' : row[h]) + '</td>');\n }\n parts.push('</tr>');\n }\n parts.push('</tbody></table>');\n return parts.join('');\n}\n\n// ===== Logger =====\n\nconst log = {\n info: function (msg, meta) { console.log('[INFO] ' + msg + (meta !== undefined ? ' ' + _safeStringify(meta) : '')); },\n warn: function (msg, meta) { console.warn('[WARN] ' + msg + (meta !== undefined ? ' ' + _safeStringify(meta) : '')); },\n error: function (msg, meta) { console.error('[ERROR] ' + msg + (meta !== undefined ? ' ' + _safeStringify(meta) : '')); },\n debug: function (msg, meta) { console.log('[DEBUG] ' + msg + (meta !== undefined ? ' ' + _safeStringify(meta) : '')); },\n};\n\n// ===== Core exports =====\n\nconst _exports = {\n // HTTP\n fetch: fetch,\n get: get,\n post: post,\n put: put,\n head: head,\n del: del,\n // File I/O\n readJson: readJson,\n writeJson: writeJson,\n readText: readText,\n writeText: writeText,\n appendText: appendText,\n readCsv: readCsv,\n writeCsv: writeCsv,\n exists: exists,\n listDir: listDir,\n // Data\n stats: stats,\n sum: sum,\n avg: avg,\n groupBy: groupBy,\n sortBy: sortBy,\n unique: unique,\n chunk: chunk,\n range: range,\n pick: pick,\n omit: omit,\n flatten: flatten,\n // Date\n formatDate: formatDate,\n parseDate: parseDate,\n now: now,\n daysAgo: daysAgo,\n daysLater: daysLater,\n dateDiff: dateDiff,\n // String\n template: template,\n slugify: slugify,\n truncate: truncate,\n pad: pad,\n // Table\n markdownTable: markdownTable,\n htmlTable: htmlTable,\n // Logger\n log: log,\n // fs/path passthrough for convenience\n fs: fs,\n path: path,\n};\n\nmodule.exports = _exports;\n\n// ===== Load extensions from _prelude_ext/ =====\n// \u6269\u5C55\u6A21\u5757\u901A\u8FC7 module.exports \u5BFC\u51FA\u5BF9\u8C61\uFF0C\u52A0\u8F7D\u65F6\u5408\u5E76\u8FDB sb\uFF08\u540C\u540D\u8986\u76D6\u6838\u5FC3\uFF0C\u4FBF\u4E8E\u5B9A\u5236\uFF09\n(function loadPreludeExtensions() {\n try {\n const extDir = path.join(__dirname, '_prelude_ext');\n if (!fs.existsSync(extDir)) return;\n const extFiles = fs.readdirSync(extDir).filter(function (f) {\n return f.endsWith('.cjs') && f !== 'index.cjs';\n });\n for (const f of extFiles) {\n try {\n const mod = require(path.join(extDir, f));\n const ext = mod && mod.__esModule && mod.default ? mod.default : mod;\n if (ext && typeof ext === 'object') {\n for (const key of Object.keys(ext)) {\n _exports[key] = ext[key];\n }\n }\n } catch (e) {\n console.warn('[prelude] Failed to load extension \"' + f + '\": ' + (e && e.message));\n }\n }\n } catch (e) { /* ignore extension load errors */ }\n})();\n";
|
|
53
|
+
export declare const SANDBOX_PRELUDE_SOURCE = "'use strict';\n// ============================================================\n// Sandbox Prelude - Auto-loaded as global `sb` in Node.js scripts\n// Common utilities so you only write business logic.\n// Loads extensions from _prelude_ext/ at require time.\n// ============================================================\n\nconst fs = require('fs');\nconst path = require('path');\nconst https = require('https');\nconst http = require('http');\nconst { URL } = require('url');\n\n// ===== Helpers =====\n\n// Safe JSON stringify (handles circular references)\nfunction _safeStringify(v) {\n try { return JSON.stringify(v); } catch (e) { return String(v); }\n}\n\n// ===== HTTP =====\n\n/**\n * fetch - HTTP request with timeout, JSON parsing and redirect following\n * @param {string} url - Request URL\n * @param {object} options - { method, headers, body, json, params, timeout, maxRedirects }\n * @returns {Promise<{status, headers, data, text}>}\n */\nasync function fetch(url, options = {}, _depth = 0) {\n const {\n method = 'GET',\n headers = {},\n body,\n json,\n timeout = 30000,\n params,\n maxRedirects = 3,\n } = options;\n let targetUrl = url;\n if (params) {\n const u = new URL(targetUrl);\n for (const [k, v] of Object.entries(params)) u.searchParams.set(k, String(v));\n targetUrl = u.toString();\n }\n const u = new URL(targetUrl);\n const lib = u.protocol === 'https:' ? https : http;\n const reqHeaders = { 'User-Agent': 'workai-sandbox/1.0', ...headers };\n let reqBody = body;\n if (json !== undefined) {\n reqBody = JSON.stringify(json);\n reqHeaders['Content-Type'] = reqHeaders['Content-Type'] || 'application/json';\n }\n return new Promise((resolve, reject) => {\n const req = lib.request(u, { method, headers: reqHeaders, timeout }, (res) => {\n const status = res.statusCode;\n // Follow redirects (301/302/303/307/308)\n if (\n (status === 301 || status === 302 || status === 303 || status === 307 || status === 308) &&\n res.headers.location &&\n _depth < maxRedirects\n ) {\n res.resume(); // discard response body\n const nextUrl = new URL(res.headers.location, targetUrl).toString();\n const nextMethod = status === 303 ? 'GET' : method;\n const nextBody = status === 303 ? undefined : body;\n const nextJson = status === 303 ? undefined : json;\n resolve(fetch(nextUrl, { ...options, method: nextMethod, body: nextBody, json: nextJson }, _depth + 1));\n return;\n }\n const chunks = [];\n res.on('data', (c) => chunks.push(c));\n res.on('end', () => {\n const buf = Buffer.concat(chunks);\n const text = buf.toString('utf-8');\n let data = text;\n const ct = (res.headers['content-type'] || '').toLowerCase();\n if (ct.includes('application/json') || ct.includes('+json')) {\n try { data = JSON.parse(text); } catch (e) { /* keep text */ }\n }\n resolve({ status: status, headers: res.headers, data, text });\n });\n });\n req.on('error', reject);\n req.on('timeout', () => { req.destroy(new Error('Request timeout after ' + timeout + 'ms')); });\n if (reqBody) req.write(reqBody);\n req.end();\n });\n}\n\n/** GET request shorthand */\nasync function get(url, options = {}) {\n return fetch(url, { ...options, method: 'GET' });\n}\n\n/** POST request shorthand (auto-JSON for object bodies) */\nasync function post(url, body, options = {}) {\n const isJson = typeof body === 'object' && body !== null && !Buffer.isBuffer(body);\n return fetch(url, {\n ...options,\n method: 'POST',\n json: isJson ? body : undefined,\n body: isJson ? undefined : body,\n });\n}\n\n/** PUT request shorthand (auto-JSON for object bodies) */\nasync function put(url, body, options = {}) {\n const isJson = typeof body === 'object' && body !== null && !Buffer.isBuffer(body);\n return fetch(url, {\n ...options,\n method: 'PUT',\n json: isJson ? body : undefined,\n body: isJson ? undefined : body,\n });\n}\n\n/** HEAD request shorthand */\nasync function head(url, options = {}) {\n return fetch(url, { ...options, method: 'HEAD' });\n}\n\n/** DELETE request shorthand */\nasync function del(url, options = {}) {\n return fetch(url, { ...options, method: 'DELETE' });\n}\n\n// ===== File I/O =====\n\nfunction readJson(file) {\n const content = fs.readFileSync(file, 'utf-8');\n return JSON.parse(content);\n}\n\nfunction writeJson(file, data, indent = 2) {\n const dir = path.dirname(file);\n if (dir && dir !== '.') try { fs.mkdirSync(dir, { recursive: true }); } catch (e) {}\n fs.writeFileSync(file, JSON.stringify(data, null, indent), 'utf-8');\n}\n\nfunction readText(file, encoding = 'utf-8') {\n return fs.readFileSync(file, encoding);\n}\n\nfunction writeText(file, content) {\n const dir = path.dirname(file);\n if (dir && dir !== '.') try { fs.mkdirSync(dir, { recursive: true }); } catch (e) {}\n fs.writeFileSync(file, content, 'utf-8');\n}\n\nfunction appendText(file, content) {\n const dir = path.dirname(file);\n if (dir && dir !== '.') try { fs.mkdirSync(dir, { recursive: true }); } catch (e) {}\n fs.appendFileSync(file, content, 'utf-8');\n}\n\nfunction exists(p) {\n try { return fs.existsSync(p); } catch (e) { return false; }\n}\n\nfunction listDir(dir) {\n return fs.readdirSync(dir).map(function (name) {\n let stat;\n try { stat = fs.statSync(path.join(dir, name)); } catch (e) { return name; }\n return stat.isDirectory() ? name + '/' : name;\n });\n}\n\n/**\n * readCsv - parse CSV file into array of objects\n * @param {string} file - CSV file path\n * @param {object} opts - { delimiter, headers }\n * headers: \u81EA\u5B9A\u4E49\u8868\u5934\u6570\u7EC4\uFF1B\u82E5\u63D0\u4F9B\u5219\u9996\u884C\u88AB\u89C6\u4E3A\u6570\u636E\u884C\uFF0C\u5426\u5219\u9996\u884C\u4F5C\u4E3A\u8868\u5934\n * @returns {Array<object>}\n */\nfunction readCsv(file, opts = {}) {\n const delimiter = opts.delimiter || ',';\n const content = fs.readFileSync(file, 'utf-8');\n const lines = content.split(/\\r?\\n/).filter(function (l) { return l.trim(); });\n if (lines.length === 0) return [];\n const parseLine = function (line) {\n const result = [];\n let cur = '';\n let inQuote = false;\n for (let i = 0; i < line.length; i++) {\n const c = line[i];\n if (c === '\"') {\n if (inQuote && line[i + 1] === '\"') { cur += '\"'; i++; }\n else inQuote = !inQuote;\n } else if (c === delimiter && !inQuote) {\n result.push(cur); cur = '';\n } else {\n cur += c;\n }\n }\n result.push(cur);\n return result.map(function (s) { return s.trim(); });\n };\n const headers = opts.headers || parseLine(lines[0]);\n const startIdx = opts.headers ? 0 : 1;\n const rows = [];\n for (let i = startIdx; i < lines.length; i++) {\n const values = parseLine(lines[i]);\n const row = {};\n headers.forEach(function (h, j) { row[h] = values[j] || ''; });\n rows.push(row);\n }\n return rows;\n}\n\n/**\n * writeCsv - write array of objects to CSV file\n * @param {string} file - Output CSV file path\n * @param {Array<object>} rows - Data rows\n * @param {object} opts - { headers, delimiter }\n */\nfunction writeCsv(file, rows, opts = {}) {\n if (!rows || rows.length === 0) {\n writeText(file, '');\n return;\n }\n const delimiter = opts.delimiter || ',';\n const headers = opts.headers || Object.keys(rows[0]);\n const escapeCell = function (val) {\n const s = String(val == null ? '' : val);\n if (s.includes(delimiter) || s.includes('\"') || s.includes('\\n')) {\n return '\"' + s.replace(/\"/g, '\"\"') + '\"';\n }\n return s;\n };\n const lines = [headers.map(escapeCell).join(delimiter)];\n for (const row of rows) {\n lines.push(headers.map(function (h) { return escapeCell(row[h]); }).join(delimiter));\n }\n writeText(file, lines.join('\\n'));\n}\n\n// ===== Data Utilities =====\n\n/** Calculate statistics: sum, avg, min, max, count, std */\nfunction stats(arr) {\n const nums = arr.map(Number).filter(function (n) { return !isNaN(n); });\n if (nums.length === 0) return { count: 0, sum: 0, avg: 0, min: 0, max: 0, std: 0 };\n const sumVal = nums.reduce(function (a, b) { return a + b; }, 0);\n const avgVal = sumVal / nums.length;\n const min = Math.min.apply(null, nums);\n const max = Math.max.apply(null, nums);\n const variance = nums.reduce(function (acc, n) { return acc + Math.pow(n - avgVal, 2); }, 0) / nums.length;\n return { count: nums.length, sum: sumVal, avg: avgVal, min: min, max: max, std: Math.sqrt(variance) };\n}\n\nfunction sum(arr) {\n return arr.reduce(function (a, b) { return a + (Number(b) || 0); }, 0);\n}\n\nfunction avg(arr) {\n if (arr.length === 0) return 0;\n return sum(arr) / arr.length;\n}\n\nfunction groupBy(arr, keyFn) {\n const result = {};\n for (const item of arr) {\n const key = typeof keyFn === 'function' ? keyFn(item) : item[keyFn];\n if (!result[key]) result[key] = [];\n result[key].push(item);\n }\n return result;\n}\n\nfunction sortBy(arr, keyFn, desc) {\n const fn = typeof keyFn === 'function' ? keyFn : function (x) { return x[keyFn]; };\n return arr.slice().sort(function (a, b) {\n const va = fn(a);\n const vb = fn(b);\n if (va < vb) return desc ? 1 : -1;\n if (va > vb) return desc ? -1 : 1;\n return 0;\n });\n}\n\nfunction unique(arr, keyFn) {\n if (!keyFn) {\n return arr.filter(function (v, i, self) { return self.indexOf(v) === i; });\n }\n const fn = typeof keyFn === 'function' ? keyFn : function (x) { return x[keyFn]; };\n const seen = new Set();\n return arr.filter(function (x) {\n const k = fn(x);\n if (seen.has(k)) return false;\n seen.add(k);\n return true;\n });\n}\n\nfunction chunk(arr, size) {\n size = Math.max(1, size || 1);\n const result = [];\n for (let i = 0; i < arr.length; i += size) {\n result.push(arr.slice(i, i + size));\n }\n return result;\n}\n\nfunction range(start, end, step) {\n step = step || 1;\n if (end === undefined) { end = start; start = 0; }\n const result = [];\n if (step > 0) {\n for (let i = start; i < end; i += step) result.push(i);\n } else {\n for (let i = start; i > end; i += step) result.push(i);\n }\n return result;\n}\n\nfunction pick(obj, keys) {\n const result = {};\n for (const k of keys) {\n if (k in obj) result[k] = obj[k];\n }\n return result;\n}\n\nfunction omit(obj, keys) {\n const keySet = new Set(keys);\n const result = {};\n for (const k of Object.keys(obj)) {\n if (!keySet.has(k)) result[k] = obj[k];\n }\n return result;\n}\n\nfunction flatten(arr) {\n return arr.reduce(function (flat, sub) { return flat.concat(Array.isArray(sub) ? sub : [sub]); }, []);\n}\n\n// ===== Date Utilities =====\n\nfunction formatDate(date, format) {\n const d = date instanceof Date ? date : new Date(date);\n if (isNaN(d.getTime())) return 'Invalid Date';\n const pad = function (n) { return n < 10 ? '0' + n : '' + n; };\n const map = {\n YYYY: d.getFullYear(),\n MM: pad(d.getMonth() + 1),\n DD: pad(d.getDate()),\n HH: pad(d.getHours()),\n mm: pad(d.getMinutes()),\n ss: pad(d.getSeconds()),\n };\n let result = format || 'YYYY-MM-DD HH:mm:ss';\n for (const k in map) {\n result = result.replace(k, map[k]);\n }\n return result;\n}\n\nfunction parseDate(str) {\n if (typeof str === 'number') return new Date(str);\n const d = new Date(str);\n if (!isNaN(d.getTime())) return d;\n const m = String(str).match(/^(\\d{4})[-\\/](\\d{1,2})[-\\/](\\d{1,2})/);\n if (m) return new Date(parseInt(m[1], 10), parseInt(m[2], 10) - 1, parseInt(m[3], 10));\n return null;\n}\n\nfunction now() {\n return new Date().toISOString();\n}\n\nfunction daysAgo(n) {\n const d = new Date();\n d.setDate(d.getDate() - n);\n return d;\n}\n\nfunction daysLater(n) {\n const d = new Date();\n d.setDate(d.getDate() + n);\n return d;\n}\n\nfunction dateDiff(d1, d2, unit) {\n const date1 = d1 instanceof Date ? d1 : new Date(d1);\n const date2 = d2 instanceof Date ? d2 : new Date(d2);\n const diffMs = Math.abs(date2.getTime() - date1.getTime());\n unit = unit || 'days';\n switch (unit) {\n case 'seconds': return Math.floor(diffMs / 1000);\n case 'minutes': return Math.floor(diffMs / 60000);\n case 'hours': return Math.floor(diffMs / 3600000);\n case 'days': return Math.floor(diffMs / 86400000);\n default: return diffMs;\n }\n}\n\n// ===== String Utilities =====\n\n/**\n * template - Simple template interpolation\n * @param {string} str - Template string with {variable} or {a.b.c} placeholders\n * @param {object} data - Data object\n * @returns {string}\n */\nfunction template(str, data) {\n return str.replace(/\\{([^}]+)\\}/g, function (match, key) {\n const value = key.trim().split('.').reduce(function (obj, k) {\n return obj && obj[k] !== undefined ? obj[k] : undefined;\n }, data);\n return value !== undefined && value !== null ? String(value) : '';\n });\n}\n\nfunction slugify(str) {\n return String(str)\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\nfunction truncate(str, len, suffix) {\n if (String(str).length <= len) return String(str);\n return String(str).substring(0, len) + (suffix || '...');\n}\n\nfunction pad(str, len, char, right) {\n str = String(str);\n char = char || ' ';\n const diff = len - str.length;\n if (diff <= 0) return str;\n const padding = char.repeat(diff);\n return right ? str + padding : padding + str;\n}\n\n// ===== Table Generators =====\n\n/**\n * markdownTable - Generate markdown table from array of objects\n * @param {Array<object>} rows - Data rows\n * @param {Array<string>} headers - Optional custom headers (defaults to Object.keys of first row)\n * @returns {string} Markdown table string\n */\nfunction markdownTable(rows, headers) {\n if (!rows || rows.length === 0) return '';\n headers = headers || Object.keys(rows[0]);\n const lines = [];\n lines.push('| ' + headers.join(' | ') + ' |');\n lines.push('| ' + headers.map(function () { return '---'; }).join(' | ') + ' |');\n for (const row of rows) {\n lines.push('| ' + headers.map(function (h) {\n return String(row[h] == null ? '' : row[h]).replace(/\\|/g, '\\\\|');\n }).join(' | ') + ' |');\n }\n return lines.join('\\n');\n}\n\n/**\n * htmlTable - Generate HTML table from array of objects\n * @param {Array<object>} rows - Data rows\n * @param {object} opts - { headers, tableClass }\n * @returns {string} HTML table string\n */\nfunction htmlTable(rows, opts = {}) {\n if (!rows || rows.length === 0) return '<table><tbody></tbody></table>';\n const headers = opts.headers || Object.keys(rows[0]);\n const tableClass = opts.tableClass ? ' class=\"' + opts.tableClass + '\"' : '';\n const parts = ['<table' + tableClass + '>'];\n parts.push('<thead><tr>');\n for (const h of headers) {\n parts.push('<th>' + h + '</th>');\n }\n parts.push('</tr></thead><tbody>');\n for (const row of rows) {\n parts.push('<tr>');\n for (const h of headers) {\n parts.push('<td>' + String(row[h] == null ? '' : row[h]) + '</td>');\n }\n parts.push('</tr>');\n }\n parts.push('</tbody></table>');\n return parts.join('');\n}\n\n// ===== Logger =====\n\nconst log = {\n info: function (msg, meta) { console.log('[INFO] ' + msg + (meta !== undefined ? ' ' + _safeStringify(meta) : '')); },\n warn: function (msg, meta) { console.warn('[WARN] ' + msg + (meta !== undefined ? ' ' + _safeStringify(meta) : '')); },\n error: function (msg, meta) { console.error('[ERROR] ' + msg + (meta !== undefined ? ' ' + _safeStringify(meta) : '')); },\n debug: function (msg, meta) { console.log('[DEBUG] ' + msg + (meta !== undefined ? ' ' + _safeStringify(meta) : '')); },\n};\n\n// ===== Util =====\n\nfunction sleep(ms) {\n return new Promise(function (resolve) { setTimeout(resolve, ms); });\n}\n\nfunction uuid() {\n return require('crypto').randomUUID();\n}\n\nfunction hash(value, algo) {\n return require('crypto').createHash(algo || 'sha256').update(String(value)).digest('hex');\n}\n\nfunction randomInt(minValue, maxValue) {\n minValue = minValue === undefined ? 0 : minValue;\n maxValue = maxValue === undefined ? 100 : maxValue;\n return Math.floor(Math.random() * (maxValue - minValue + 1)) + minValue;\n}\n\nfunction randomFloat(minValue, maxValue) {\n minValue = minValue === undefined ? 0 : minValue;\n maxValue = maxValue === undefined ? 1 : maxValue;\n return Math.random() * (maxValue - minValue) + minValue;\n}\n\nfunction randomChoice(seq) {\n if (!Array.isArray(seq)) throw new Error('randomChoice \u9700\u8981\u6570\u7EC4');\n return seq[Math.floor(Math.random() * seq.length)];\n}\n\n// \u5185\u5B58\u7F13\u5B58\uFF08\u5E26 TTL\uFF0C\u5355\u4F4D\u79D2\uFF09\uFF0Csb.cache(key, ttl).get(factory)\nconst _cacheStore = {};\nfunction cache(key, ttl) {\n return {\n get: function (factory) {\n const nowTs = Date.now();\n const entry = _cacheStore[key];\n if (entry && (entry.expireAt === null || entry.expireAt > nowTs)) return entry.value;\n const value = typeof factory === 'function' ? factory() : factory;\n _cacheStore[key] = { value: value, expireAt: ttl ? nowTs + ttl * 1000 : null };\n return value;\n },\n clear: function () { delete _cacheStore[key]; },\n };\n}\n\n// ===== Core exports =====\n\nconst _exports = {\n // HTTP\n fetch: fetch,\n get: get,\n post: post,\n put: put,\n head: head,\n del: del,\n // File I/O\n readJson: readJson,\n writeJson: writeJson,\n readText: readText,\n writeText: writeText,\n appendText: appendText,\n readCsv: readCsv,\n writeCsv: writeCsv,\n exists: exists,\n listDir: listDir,\n // Data\n stats: stats,\n sum: sum,\n avg: avg,\n groupBy: groupBy,\n sortBy: sortBy,\n unique: unique,\n chunk: chunk,\n range: range,\n pick: pick,\n omit: omit,\n flatten: flatten,\n // Date\n formatDate: formatDate,\n parseDate: parseDate,\n now: now,\n daysAgo: daysAgo,\n daysLater: daysLater,\n dateDiff: dateDiff,\n // String\n template: template,\n slugify: slugify,\n truncate: truncate,\n pad: pad,\n // Table\n markdownTable: markdownTable,\n htmlTable: htmlTable,\n // Logger\n log: log,\n // Util\n sleep: sleep,\n uuid: uuid,\n hash: hash,\n randomInt: randomInt,\n randomFloat: randomFloat,\n randomChoice: randomChoice,\n cache: cache,\n // fs/path passthrough for convenience\n fs: fs,\n path: path,\n};\n\nmodule.exports = _exports;\n\n// ===== Load extensions from _prelude_ext/ =====\n// \u6269\u5C55\u6A21\u5757\u901A\u8FC7 module.exports \u5BFC\u51FA\u5BF9\u8C61\uFF0C\u52A0\u8F7D\u65F6\u5408\u5E76\u8FDB sb\uFF08\u540C\u540D\u8986\u76D6\u6838\u5FC3\uFF0C\u4FBF\u4E8E\u5B9A\u5236\uFF09\n(function loadPreludeExtensions() {\n try {\n const extDir = path.join(__dirname, '_prelude_ext');\n if (!fs.existsSync(extDir)) return;\n const extFiles = fs.readdirSync(extDir).filter(function (f) {\n return f.endsWith('.cjs') && f !== 'index.cjs';\n });\n for (const f of extFiles) {\n try {\n const mod = require(path.join(extDir, f));\n const ext = mod && mod.__esModule && mod.default ? mod.default : mod;\n if (ext && typeof ext === 'object') {\n for (const key of Object.keys(ext)) {\n _exports[key] = ext[key];\n }\n }\n } catch (e) {\n console.warn('[prelude] Failed to load extension \"' + f + '\": ' + (e && e.message));\n }\n }\n } catch (e) { /* ignore extension load errors */ }\n})();\n";
|
|
54
54
|
//# sourceMappingURL=sandbox-prelude.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox-prelude.d.ts","sourceRoot":"","sources":["../../../src/sandbox/sandbox-prelude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,MAAM,WAAW,uBAAuB;IACtC,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;CAChB;AAID;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAalF;AAED,kBAAkB;AAClB,wBAAgB,2BAA2B,IAAI,uBAAuB,EAAE,CAEvE;AAED,yBAAyB;AACzB,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,
|
|
1
|
+
{"version":3,"file":"sandbox-prelude.d.ts","sourceRoot":"","sources":["../../../src/sandbox/sandbox-prelude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,MAAM,WAAW,uBAAuB;IACtC,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;CAChB;AAID;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAalF;AAED,kBAAkB;AAClB,wBAAgB,2BAA2B,IAAI,uBAAuB,EAAE,CAEvE;AAED,yBAAyB;AACzB,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,uwmBAmnBlC,CAAC"}
|
|
@@ -555,6 +555,53 @@ const log = {
|
|
|
555
555
|
debug: function (msg, meta) { console.log('[DEBUG] ' + msg + (meta !== undefined ? ' ' + _safeStringify(meta) : '')); },
|
|
556
556
|
};
|
|
557
557
|
|
|
558
|
+
// ===== Util =====
|
|
559
|
+
|
|
560
|
+
function sleep(ms) {
|
|
561
|
+
return new Promise(function (resolve) { setTimeout(resolve, ms); });
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function uuid() {
|
|
565
|
+
return require('crypto').randomUUID();
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function hash(value, algo) {
|
|
569
|
+
return require('crypto').createHash(algo || 'sha256').update(String(value)).digest('hex');
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function randomInt(minValue, maxValue) {
|
|
573
|
+
minValue = minValue === undefined ? 0 : minValue;
|
|
574
|
+
maxValue = maxValue === undefined ? 100 : maxValue;
|
|
575
|
+
return Math.floor(Math.random() * (maxValue - minValue + 1)) + minValue;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function randomFloat(minValue, maxValue) {
|
|
579
|
+
minValue = minValue === undefined ? 0 : minValue;
|
|
580
|
+
maxValue = maxValue === undefined ? 1 : maxValue;
|
|
581
|
+
return Math.random() * (maxValue - minValue) + minValue;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function randomChoice(seq) {
|
|
585
|
+
if (!Array.isArray(seq)) throw new Error('randomChoice 需要数组');
|
|
586
|
+
return seq[Math.floor(Math.random() * seq.length)];
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// 内存缓存(带 TTL,单位秒),sb.cache(key, ttl).get(factory)
|
|
590
|
+
const _cacheStore = {};
|
|
591
|
+
function cache(key, ttl) {
|
|
592
|
+
return {
|
|
593
|
+
get: function (factory) {
|
|
594
|
+
const nowTs = Date.now();
|
|
595
|
+
const entry = _cacheStore[key];
|
|
596
|
+
if (entry && (entry.expireAt === null || entry.expireAt > nowTs)) return entry.value;
|
|
597
|
+
const value = typeof factory === 'function' ? factory() : factory;
|
|
598
|
+
_cacheStore[key] = { value: value, expireAt: ttl ? nowTs + ttl * 1000 : null };
|
|
599
|
+
return value;
|
|
600
|
+
},
|
|
601
|
+
clear: function () { delete _cacheStore[key]; },
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
558
605
|
// ===== Core exports =====
|
|
559
606
|
|
|
560
607
|
const _exports = {
|
|
@@ -604,6 +651,14 @@ const _exports = {
|
|
|
604
651
|
htmlTable: htmlTable,
|
|
605
652
|
// Logger
|
|
606
653
|
log: log,
|
|
654
|
+
// Util
|
|
655
|
+
sleep: sleep,
|
|
656
|
+
uuid: uuid,
|
|
657
|
+
hash: hash,
|
|
658
|
+
randomInt: randomInt,
|
|
659
|
+
randomFloat: randomFloat,
|
|
660
|
+
randomChoice: randomChoice,
|
|
661
|
+
cache: cache,
|
|
607
662
|
// fs/path passthrough for convenience
|
|
608
663
|
fs: fs,
|
|
609
664
|
path: path,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox-prelude.js","sourceRoot":"","sources":["../../../src/sandbox/sandbox-prelude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAWH,MAAM,0BAA0B,GAA8B,EAAE,CAAC;AAEjE;;;;;;GAMG;AACH,MAAM,UAAU,+BAA+B,CAAC,IAAY,EAAE,MAAc;IAC1E,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,gCAAgC,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,sBAAsB,IAAI,sCAAsC,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,GAAG,GAAG,0BAA0B,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACzE,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;QACb,0BAA0B,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACrD,CAAC;SAAM,CAAC;QACN,0BAA0B,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAED,kBAAkB;AAClB,MAAM,UAAU,2BAA2B;IACzC,OAAO,0BAA0B,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,yBAAyB;AACzB,MAAM,UAAU,6BAA6B;IAC3C,0BAA0B,CAAC,MAAM,GAAG,CAAC,CAAC;AACxC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG
|
|
1
|
+
{"version":3,"file":"sandbox-prelude.js","sourceRoot":"","sources":["../../../src/sandbox/sandbox-prelude.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAWH,MAAM,0BAA0B,GAA8B,EAAE,CAAC;AAEjE;;;;;;GAMG;AACH,MAAM,UAAU,+BAA+B,CAAC,IAAY,EAAE,MAAc;IAC1E,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,gCAAgC,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,sBAAsB,IAAI,sCAAsC,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,GAAG,GAAG,0BAA0B,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACzE,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;QACb,0BAA0B,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACrD,CAAC;SAAM,CAAC;QACN,0BAA0B,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAED,kBAAkB;AAClB,MAAM,UAAU,2BAA2B;IACzC,OAAO,0BAA0B,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,yBAAyB;AACzB,MAAM,UAAU,6BAA6B;IAC3C,0BAA0B,CAAC,MAAM,GAAG,CAAC,CAAC;AACxC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmnBrC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cicctencent/agent-server",
|
|
3
|
-
"version": "0.2
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Server-layer toolkit for agent-core — Agent initialization, SSE streaming, built-in tools, connectors, chat/settings/memory services with SPI extensibility.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@apidevtools/swagger-parser": "^12.1.0",
|
|
31
|
-
"@cicctencent/agent-core": "^0.
|
|
31
|
+
"@cicctencent/agent-core": "^1.0.2",
|
|
32
32
|
"axios": "^1.18.1",
|
|
33
33
|
"cheerio": "^1.2.0",
|
|
34
34
|
"docx": "^9.7.1",
|