@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.
Files changed (37) hide show
  1. package/dist/cjs/automation/node-task-runtime.d.ts.map +1 -1
  2. package/dist/cjs/automation/node-task-runtime.js +196 -8
  3. package/dist/cjs/automation/node-task-runtime.js.map +1 -1
  4. package/dist/cjs/automation/node-task-validator.d.ts +10 -0
  5. package/dist/cjs/automation/node-task-validator.d.ts.map +1 -1
  6. package/dist/cjs/automation/node-task-validator.js +10 -0
  7. package/dist/cjs/automation/node-task-validator.js.map +1 -1
  8. package/dist/cjs/automation/python-prelude.d.ts +1 -1
  9. package/dist/cjs/automation/python-prelude.d.ts.map +1 -1
  10. package/dist/cjs/automation/python-prelude.js +196 -1
  11. package/dist/cjs/automation/python-prelude.js.map +1 -1
  12. package/dist/cjs/automation/python-task-runtime.d.ts.map +1 -1
  13. package/dist/cjs/automation/python-task-runtime.js +56 -4
  14. package/dist/cjs/automation/python-task-runtime.js.map +1 -1
  15. package/dist/cjs/sandbox/sandbox-prelude.d.ts +1 -1
  16. package/dist/cjs/sandbox/sandbox-prelude.d.ts.map +1 -1
  17. package/dist/cjs/sandbox/sandbox-prelude.js +55 -0
  18. package/dist/cjs/sandbox/sandbox-prelude.js.map +1 -1
  19. package/dist/esm/automation/node-task-runtime.d.ts.map +1 -1
  20. package/dist/esm/automation/node-task-runtime.js +196 -8
  21. package/dist/esm/automation/node-task-runtime.js.map +1 -1
  22. package/dist/esm/automation/node-task-validator.d.ts +10 -0
  23. package/dist/esm/automation/node-task-validator.d.ts.map +1 -1
  24. package/dist/esm/automation/node-task-validator.js +10 -0
  25. package/dist/esm/automation/node-task-validator.js.map +1 -1
  26. package/dist/esm/automation/python-prelude.d.ts +1 -1
  27. package/dist/esm/automation/python-prelude.d.ts.map +1 -1
  28. package/dist/esm/automation/python-prelude.js +196 -1
  29. package/dist/esm/automation/python-prelude.js.map +1 -1
  30. package/dist/esm/automation/python-task-runtime.d.ts.map +1 -1
  31. package/dist/esm/automation/python-task-runtime.js +56 -4
  32. package/dist/esm/automation/python-task-runtime.js.map +1 -1
  33. package/dist/esm/sandbox/sandbox-prelude.d.ts +1 -1
  34. package/dist/esm/sandbox/sandbox-prelude.d.ts.map +1 -1
  35. package/dist/esm/sandbox/sandbox-prelude.js +55 -0
  36. package/dist/esm/sandbox/sandbox-prelude.js.map +1 -1
  37. 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 days_go(n):
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2TpC,CAAC"}
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;IA0F5B,OAAO,CAAC,kBAAkB;IAkB1B,OAAO,CAAC,iBAAiB;CAU1B;AAID,wBAAgB,gCAAgC,IAAI,6BAA6B,CAGhF"}
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, str(c))),
201
- "write_text": staticmethod(lambda f, c: sb.write_text(f, str(c))),
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
- __log("error", str(e))
264
+ __duration_ms = int((time.time() - __start) * 1000)
265
+ __log("error", str(e), {"durationMs": __duration_ms})
222
266
  traceback.print_exc()
223
- print("__NODE_AUTOMATION_ERROR__" + json.dumps({"message": str(e)}, ensure_ascii=False, default=str))
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":"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"}
@@ -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,wkjBA4jBlC,CAAC"}
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4jBrC,CAAC"}
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.97",
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.1.124",
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",