@cicctencent/agent-server 1.0.1 → 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
@@ -28,12 +28,20 @@ import datetime as _dt
28
28
  import urllib.request
29
29
  import urllib.error
30
30
  import urllib.parse
31
+ import threading
32
+ import hashlib
33
+ import uuid as _uuid
34
+ import random as _random
31
35
  from typing import Any, Dict, List, Optional
32
36
 
33
37
  # Agent 调用端点(由运行时注入)
34
38
  _AGENT_ENDPOINT = "__AGENT_ENDPOINT__"
35
39
  _AGENT_CONFIGURED = bool(_AGENT_ENDPOINT)
36
40
 
41
+ # 内存缓存(带 TTL),用于 sb.cache
42
+ _CACHE_STORE: Dict[str, Any] = {}
43
+ _CACHE_LOCK = threading.Lock()
44
+
37
45
 
38
46
  class _Logger:
39
47
  def __init__(self, sink):
@@ -235,14 +243,85 @@ def parse_date(text, fmt=None):
235
243
  return _dt.datetime.fromisoformat(text)
236
244
 
237
245
 
238
- def days_go(n):
246
+ def days_ago(n):
239
247
  return _dt.datetime.now() - _dt.timedelta(days=n)
240
248
 
241
249
 
250
+ # 兼容旧名(文档与示例统一使用 sb.days_ago)
251
+ days_go = days_ago
252
+
253
+
242
254
  def days_later(n):
243
255
  return _dt.datetime.now() + _dt.timedelta(days=n)
244
256
 
245
257
 
258
+ # ===== 工具函数(Util) =====
259
+
260
+ def sleep(seconds):
261
+ """阻塞当前协程/线程指定秒数。"""
262
+ time.sleep(seconds)
263
+
264
+
265
+ def uuid() -> str:
266
+ """生成 UUID4 字符串。"""
267
+ return str(_uuid.uuid4())
268
+
269
+
270
+ def hash(value, algo="sha256") -> str:
271
+ """计算字符串/对象的哈希(默认 sha256)。"""
272
+ h = hashlib.new(algo)
273
+ h.update(str(value).encode("utf-8"))
274
+ return h.hexdigest()
275
+
276
+
277
+ def random_int(min_value=0, max_value=100) -> int:
278
+ """返回 [min_value, max_value] 区间内的随机整数。"""
279
+ return _random.randint(min_value, max_value)
280
+
281
+
282
+ def random_choice(seq):
283
+ """从序列中随机取一个元素。"""
284
+ return _random.choice(seq)
285
+
286
+
287
+ def random_float(min_value=0.0, max_value=1.0) -> float:
288
+ """返回 [min_value, max_value] 区间内的随机浮点数。"""
289
+ return _random.uniform(min_value, max_value)
290
+
291
+
292
+ def cache(key: str, ttl: Optional[float] = None):
293
+ """带 TTL 的内存缓存访问器。
294
+
295
+ 用法:
296
+ hits = sb.cache("api:hot", ttl=60).get(lambda: sb.get("/hot").get("data"))
297
+ sb.cache("api:hot").clear() # 清除该 key
298
+
299
+ ttl 为 None 表示永不过期(进程内)。
300
+ 返回对象含 get(factory) / clear() 两个方法。
301
+ """
302
+ class _CacheEntry:
303
+ __slots__ = ("value", "expire_at")
304
+ def __init__(self, value, expire_at):
305
+ self.value = value
306
+ self.expire_at = expire_at
307
+
308
+ def _get(factory):
309
+ now_ts = time.time()
310
+ with _CACHE_LOCK:
311
+ entry = _CACHE_STORE.get(key)
312
+ if entry is not None and (entry.expire_at is None or entry.expire_at > now_ts):
313
+ return entry.value
314
+ value = factory() if callable(factory) else factory
315
+ _CACHE_STORE[key] = _CacheEntry(value, now_ts + ttl if ttl else None)
316
+ return value
317
+
318
+ def _clear():
319
+ with _CACHE_LOCK:
320
+ _CACHE_STORE.pop(key, None)
321
+
322
+ return type("CacheHandle", (), {"get": _get, "clear": _clear})()
323
+
324
+
246
325
  # ===== Agent =====
247
326
 
248
327
  class _Agent:
@@ -255,11 +334,18 @@ class _Agent:
255
334
  - 流式逐 token 返回,避免非流式 HTTP 长等待超时;
256
335
  - 服务端每 3s 发送心跳,保持连接活跃;
257
336
  - timeout 为单次读取超时(默认 600s),流式心跳保证不会因空闲而超时。
337
+
338
+ options:
339
+ - timeout : 单次读取超时(秒)
340
+ - contextId : 会话上下文 id(用于多轮记忆)
341
+ - profile : Agent profile(名称或 id)
342
+ - json : True 时把最终文本按 JSON 解析并返回对象(自动去除 \`\`\` 代码围栏)
258
343
  """
259
344
  if not _AGENT_CONFIGURED:
260
345
  raise RuntimeError("Agent endpoint not configured")
261
346
  options = options or {}
262
347
  timeout = options.get("timeout", 600)
348
+ json_mode = options.get("json", False)
263
349
  payload = {
264
350
  "jsonrpc": "2.0",
265
351
  "id": options.get("id", 1),
@@ -323,8 +409,117 @@ class _Agent:
323
409
  pass
324
410
  if task_failed:
325
411
  raise RuntimeError("Agent task failed: %s" % (task_error or "unknown"))
412
+ if json_mode:
413
+ return _parse_json_text(final_text)
326
414
  return final_text
327
415
 
416
+ def stream(self, prompt, options=None):
417
+ """流式调用 Agent,增量 yield 文本片段(delta)。
418
+
419
+ A2A 的 message 事件通常携带「到目前为止的完整文本」,故与前一次文本做前缀
420
+ 比较,只输出新增部分(delta),避免重复。
421
+
422
+ options 同 ask(timeout / contextId / profile / json)。
423
+ json=True 时,流式结束后额外 yield 一次解析后的对象(最后一次 yield 为 dict)。
424
+ """
425
+ if not _AGENT_CONFIGURED:
426
+ raise RuntimeError("Agent endpoint not configured")
427
+ options = options or {}
428
+ timeout = options.get("timeout", 600)
429
+ json_mode = options.get("json", False)
430
+ payload = {
431
+ "jsonrpc": "2.0",
432
+ "id": options.get("id", 1),
433
+ "method": "tasks/sendSubscribe",
434
+ "params": {
435
+ "message": {
436
+ "role": "user",
437
+ "parts": [{"kind": "text", "text": str(prompt)}],
438
+ },
439
+ "contextId": options.get("contextId"),
440
+ "profile": options.get("profile"),
441
+ },
442
+ }
443
+ data = json.dumps(payload).encode("utf-8")
444
+ req = urllib.request.Request(
445
+ _AGENT_ENDPOINT,
446
+ data=data,
447
+ headers={
448
+ "Content-Type": "application/json",
449
+ "Accept": "text/event-stream",
450
+
451
+ "User-Agent": "workai-sandbox/1.0",
452
+ },
453
+ method="POST",
454
+ )
455
+ last = ""
456
+ buf = b""
457
+ try:
458
+ resp = urllib.request.urlopen(req, timeout=timeout)
459
+ except urllib.error.HTTPError as e:
460
+ raise RuntimeError("Agent call failed (HTTP %s)" % e.code)
461
+ try:
462
+ for raw in resp:
463
+ buf += raw
464
+ while b"\n\n" in buf:
465
+ block, buf = buf.split(b"\n\n", 1)
466
+ text = block.decode("utf-8", errors="replace")
467
+ for line in text.splitlines():
468
+ if not line.startswith("data:"):
469
+ continue
470
+ payload_str = line[5:].strip()
471
+ if not payload_str:
472
+ continue
473
+ try:
474
+ msg = json.loads(payload_str)
475
+ except Exception:
476
+ continue
477
+ result = msg.get("result") or {}
478
+ etype = result.get("type")
479
+ edata = result.get("data")
480
+ if etype == "error":
481
+ err_msg = edata.get("message") if isinstance(edata, dict) else str(edata)
482
+ raise RuntimeError("Agent error: %s" % err_msg)
483
+ t = ""
484
+ if etype == "message" and isinstance(edata, dict):
485
+ for p in (edata.get("parts") or []):
486
+ if (p.get("kind") == "text" or p.get("type") == "text") and p.get("text"):
487
+ t = p["text"]
488
+ elif etype == "task" and isinstance(edata, dict) and edata.get("state") == "failed":
489
+ raise RuntimeError("Agent task failed: %s" % (edata.get("error") or "unknown"))
490
+ if not t:
491
+ continue
492
+ # 前缀差量:只输出新增内容
493
+ if t.startswith(last):
494
+ delta = t[len(last):]
495
+ if delta:
496
+ yield delta
497
+ else:
498
+ yield t
499
+ last = t
500
+ finally:
501
+ try:
502
+ resp.close()
503
+ except Exception:
504
+ pass
505
+ if json_mode and last:
506
+ yield _parse_json_text(last)
507
+
508
+
509
+ def _parse_json_text(text):
510
+ """把 Agent 返回文本按 JSON 解析(自动去除 \`\`\`json 代码围栏)。"""
511
+ s = (text or "").strip()
512
+ if s.startswith("\`\`\`"):
513
+ # 去除首行围栏(\`\`\`json / \`\`\`)
514
+ s = s.split("\n", 1)[1] if "\n" in s else s[3:]
515
+ if s.endswith("\`\`\`"):
516
+ s = s[:-3]
517
+ s = s.strip()
518
+ try:
519
+ return json.loads(s)
520
+ except Exception:
521
+ raise RuntimeError("Agent 返回内容不是合法 JSON: %s" % s[:200])
522
+
328
523
 
329
524
  agent = _Agent()
330
525
  `;
@@ -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;AACU,QAAA,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;AACU,QAAA,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"}
@@ -225,24 +225,64 @@ for __file, __content in (__task.get("sourceFiles") or {}).items():
225
225
  with open(__target, "w", encoding="utf-8") as __f:
226
226
  __f.write(__content)
227
227
 
228
+ # 产物大小上限(单文件默认 5MB,避免超大产物撑爆存储/响应)
229
+ ARTIFACT_MAX_BYTES = 5 * 1024 * 1024
230
+
231
+ def __artifact_guard(content):
232
+ if isinstance(content, (bytes, bytearray)):
233
+ size = len(content)
234
+ else:
235
+ size = len(str(content).encode("utf-8"))
236
+ if size > ARTIFACT_MAX_BYTES:
237
+ raise RuntimeError("产物体积超限(最大 %d 字节): 实际 %d" % (ARTIFACT_MAX_BYTES, size))
238
+ return content
239
+
228
240
  # 构造 context(与 Node 端结构对齐)
241
+ # secrets:仅暴露白名单环境变量(与 env 同源),供任务安全读取凭证,避免误用 os.environ 全量泄露。
229
242
  __context = {
230
243
  "taskId": __data["taskId"],
231
244
  "runId": __data["runId"],
232
245
  "inputs": __data["inputs"],
233
246
  "env": __data["env"],
247
+ "secrets": __data["env"],
234
248
  "workspace": __data["workspace"],
235
249
  "logger": sb._Logger(__log),
236
250
  "artifacts": type("A", (), {
237
- "writeText": staticmethod(lambda f, c: sb.write_text(f, str(c))),
238
- "write_text": staticmethod(lambda f, c: sb.write_text(f, str(c))),
251
+ "writeText": staticmethod(lambda f, c: sb.write_text(f, __artifact_guard(c))),
252
+ "write_text": staticmethod(lambda f, c: sb.write_text(f, __artifact_guard(c))),
239
253
  "writeJson": staticmethod(lambda f, d: sb.write_json(f, d)),
240
254
  "write_json": staticmethod(lambda f, d: sb.write_json(f, d)),
255
+ "readText": staticmethod(lambda f: sb.read_text(f)),
256
+ "read_text": staticmethod(lambda f: sb.read_text(f)),
257
+ "readJson": staticmethod(lambda f: sb.read_json(f)),
258
+ "read_json": staticmethod(lambda f: sb.read_json(f)),
241
259
  })(),
242
260
  "agent": sb.agent,
243
261
  }
244
262
 
263
+ def __validate_inputs(schema, inputs):
264
+ """运行前校验 inputsSchema(JSON Schema 子集:properties / required / type)。"""
265
+ if not schema or not isinstance(schema, dict):
266
+ return
267
+ props = schema.get("properties") or {}
268
+ required = schema.get("required") or []
269
+ if not isinstance(inputs, dict):
270
+ raise RuntimeError("inputs 必须是对象,但收到 %s" % type(inputs).__name__)
271
+ for key in required:
272
+ if key not in inputs:
273
+ raise RuntimeError("缺少必填输入项: %s" % key)
274
+ __type_map = {"string": str, "number": (int, float), "integer": int,
275
+ "boolean": bool, "array": list, "object": dict}
276
+ for key, val in inputs.items():
277
+ if key in props and isinstance(props[key], dict):
278
+ expected = props[key].get("type")
279
+ if expected and expected in __type_map and not isinstance(val, __type_map[expected]):
280
+ raise RuntimeError("输入项 %s 类型错误:期望 %s,实际 %s" % (key, expected, type(val).__name__))
281
+
245
282
  def __run():
283
+ # 运行前校验输入 Schema(顶层 inputsSchema 或 metadata.inputsSchema)
284
+ __schema = __task.get("inputsSchema") or (__task.get("metadata") or {}).get("inputsSchema")
285
+ __validate_inputs(__schema, __data["inputs"])
246
286
  __entry = __task["entry"]
247
287
  __mod_name = os.path.splitext(__entry)[0].replace(os.sep, ".")
248
288
  import importlib
@@ -251,13 +291,25 @@ def __run():
251
291
  raise RuntimeError("Python automation entry must define run(context)")
252
292
  return __mod.run(__context)
253
293
 
294
+ __start = time.time()
254
295
  try:
255
296
  __result = __run()
297
+ __duration_ms = int((time.time() - __start) * 1000)
298
+ __log("info", "task finished", {"durationMs": __duration_ms})
256
299
  print("__NODE_AUTOMATION_RESULT__" + json.dumps({"result": __result}, ensure_ascii=False, default=str))
257
300
  except Exception as e:
258
- __log("error", str(e))
301
+ __duration_ms = int((time.time() - __start) * 1000)
302
+ __log("error", str(e), {"durationMs": __duration_ms})
259
303
  traceback.print_exc()
260
- print("__NODE_AUTOMATION_ERROR__" + json.dumps({"message": str(e)}, ensure_ascii=False, default=str))
304
+ # 结构化错误:type / code / retryable 供 UI 友好展示与重试策略判断
305
+ __err = {
306
+ "message": str(e),
307
+ "type": type(e).__name__,
308
+ "code": "TASK_EXECUTION_FAILED",
309
+ "retryable": isinstance(e, (RuntimeError, TimeoutError, ConnectionError, OSError)),
310
+ "durationMs": __duration_ms,
311
+ }
312
+ print("__NODE_AUTOMATION_ERROR__" + json.dumps(__err, ensure_ascii=False, default=str))
261
313
  sys.exit(1)
262
314
  `;
263
315
  }
@@ -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"}
@@ -561,6 +561,53 @@ const log = {
561
561
  debug: function (msg, meta) { console.log('[DEBUG] ' + msg + (meta !== undefined ? ' ' + _safeStringify(meta) : '')); },
562
562
  };
563
563
 
564
+ // ===== Util =====
565
+
566
+ function sleep(ms) {
567
+ return new Promise(function (resolve) { setTimeout(resolve, ms); });
568
+ }
569
+
570
+ function uuid() {
571
+ return require('crypto').randomUUID();
572
+ }
573
+
574
+ function hash(value, algo) {
575
+ return require('crypto').createHash(algo || 'sha256').update(String(value)).digest('hex');
576
+ }
577
+
578
+ function randomInt(minValue, maxValue) {
579
+ minValue = minValue === undefined ? 0 : minValue;
580
+ maxValue = maxValue === undefined ? 100 : maxValue;
581
+ return Math.floor(Math.random() * (maxValue - minValue + 1)) + minValue;
582
+ }
583
+
584
+ function randomFloat(minValue, maxValue) {
585
+ minValue = minValue === undefined ? 0 : minValue;
586
+ maxValue = maxValue === undefined ? 1 : maxValue;
587
+ return Math.random() * (maxValue - minValue) + minValue;
588
+ }
589
+
590
+ function randomChoice(seq) {
591
+ if (!Array.isArray(seq)) throw new Error('randomChoice 需要数组');
592
+ return seq[Math.floor(Math.random() * seq.length)];
593
+ }
594
+
595
+ // 内存缓存(带 TTL,单位秒),sb.cache(key, ttl).get(factory)
596
+ const _cacheStore = {};
597
+ function cache(key, ttl) {
598
+ return {
599
+ get: function (factory) {
600
+ const nowTs = Date.now();
601
+ const entry = _cacheStore[key];
602
+ if (entry && (entry.expireAt === null || entry.expireAt > nowTs)) return entry.value;
603
+ const value = typeof factory === 'function' ? factory() : factory;
604
+ _cacheStore[key] = { value: value, expireAt: ttl ? nowTs + ttl * 1000 : null };
605
+ return value;
606
+ },
607
+ clear: function () { delete _cacheStore[key]; },
608
+ };
609
+ }
610
+
564
611
  // ===== Core exports =====
565
612
 
566
613
  const _exports = {
@@ -610,6 +657,14 @@ const _exports = {
610
657
  htmlTable: htmlTable,
611
658
  // Logger
612
659
  log: log,
660
+ // Util
661
+ sleep: sleep,
662
+ uuid: uuid,
663
+ hash: hash,
664
+ randomInt: randomInt,
665
+ randomFloat: randomFloat,
666
+ randomChoice: randomChoice,
667
+ cache: cache,
613
668
  // fs/path passthrough for convenience
614
669
  fs: fs,
615
670
  path: path,
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox-prelude.js","sourceRoot":"","sources":["../../../src/sandbox/sandbox-prelude.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;;;AAoBH,0EAaC;AAGD,kEAEC;AAGD,sEAEC;AAhCD,MAAM,0BAA0B,GAA8B,EAAE,CAAC;AAEjE;;;;;;GAMG;AACH,SAAgB,+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,SAAgB,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,SAAgB,6BAA6B;IAC3C,0BAA0B,CAAC,MAAM,GAAG,CAAC,CAAC;AACxC,CAAC;AAED;;;;GAIG;AACU,QAAA,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4jBrC,CAAC"}
1
+ {"version":3,"file":"sandbox-prelude.js","sourceRoot":"","sources":["../../../src/sandbox/sandbox-prelude.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;;;AAoBH,0EAaC;AAGD,kEAEC;AAGD,sEAEC;AAhCD,MAAM,0BAA0B,GAA8B,EAAE,CAAC;AAEjE;;;;;;GAMG;AACH,SAAgB,+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,SAAgB,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,SAAgB,6BAA6B;IAC3C,0BAA0B,CAAC,MAAM,GAAG,CAAC,CAAC;AACxC,CAAC;AAED;;;;GAIG;AACU,QAAA,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmnBrC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"node-task-runtime.d.ts","sourceRoot":"","sources":["../../../src/automation/node-task-runtime.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,6BAA6B,EAC7B,sBAAsB,EACtB,wBAAwB,EACxB,yBAAyB,EAC1B,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAoC,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AAEzG,MAAM,WAAW,4BAA4B;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,6BAA6B,CAAC;CACjD;AAaD,qBAAa,2BAA2B;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;gBAEpD,OAAO,GAAE,4BAAiC;IAKhD,OAAO,CACX,WAAW,EAAE,yBAAyB,EACtC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,6BAA6B,CAAC;IAqEzC;;;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;IAyEzC,8CAA8C;IAC9C,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,oBAAoB;IAQ5B,OAAO,CAAC,oBAAoB;IA4I5B,+CAA+C;IAC/C,OAAO,CAAC,cAAc;IAqBtB,gEAAgE;IAChE,OAAO,CAAC,YAAY;IASpB,wDAAwD;IACxD,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,kBAAkB;CAgB3B;AAID,wBAAgB,8BAA8B,IAAI,2BAA2B,CAG5E"}
1
+ {"version":3,"file":"node-task-runtime.d.ts","sourceRoot":"","sources":["../../../src/automation/node-task-runtime.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,6BAA6B,EAC7B,sBAAsB,EACtB,wBAAwB,EACxB,yBAAyB,EAC1B,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAoC,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AAEzG,MAAM,WAAW,4BAA4B;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,6BAA6B,CAAC;CACjD;AAaD,qBAAa,2BAA2B;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;gBAEpD,OAAO,GAAE,4BAAiC;IAKhD,OAAO,CACX,WAAW,EAAE,yBAAyB,EACtC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,6BAA6B,CAAC;IAqEzC;;;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;IAyEzC,8CAA8C;IAC9C,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,oBAAoB;IAQ5B,OAAO,CAAC,oBAAoB;IAwU5B,+CAA+C;IAC/C,OAAO,CAAC,cAAc;IAqBtB,gEAAgE;IAChE,OAAO,CAAC,YAAY;IASpB,wDAAwD;IACxD,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,kBAAkB;CAgB3B;AAID,wBAAgB,8BAA8B,IAAI,2BAA2B,CAG5E"}