oc-browser-relay 1.0.5 → 1.0.6

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 (34) hide show
  1. package/bundled-relay/index.js +301 -6
  2. package/bundled-relay/public/configs/pages/douyin-creator-publish.json +473 -0
  3. package/bundled-relay/public/configs/pages/index.json +2 -1
  4. package/bundled-relay/public/configs/pages/sycm-taobao-home.json +387 -1
  5. package/bundled-relay/public/configs/pages/taobao-item-detail.json +117 -83
  6. package/bundled-relay/public/configs/pages/xiaohongshu-creator-publish.json +2 -5
  7. package/bundled-relay/public/configs/sites/douyin-creator.json +29 -0
  8. package/bundled-relay/public/configs/sites/index.json +2 -1
  9. package/bundled-relay/public/configs/task-templates/douyin-creator-publish.json +131 -0
  10. package/bundled-relay/public/configs/task-templates/douyin-creator-save-draft.json +99 -0
  11. package/bundled-relay/public/configs/task-templates/index.json +5 -1
  12. package/bundled-relay/public/configs/task-templates/sycm-taobao-api-group.json +170 -0
  13. package/bundled-relay/public/configs/task-templates/sycm-taobao-home-live-metrics.json +17 -0
  14. package/bundled-relay/public/configs/task-templates/taobao-item-detail-comments.json +61 -21
  15. package/bundled-relay/public/configs/task-templates/wechat-get-article.json +44 -0
  16. package/bundled-relay/public/configs/task-templates/xiaohongshu-creator-publish.json +15 -0
  17. package/bundled-relay/public/configs/task-templates/xiaohongshu-creator-save-draft.json +15 -0
  18. package/bundled-relay/public/configs/task-templates/xiaohongshu-web-search-notes.json +16 -0
  19. package/bundled-relay/public/icons/icon-active-128.png +0 -0
  20. package/bundled-relay/public/icons/icon-active-48.png +0 -0
  21. package/bundled-relay/public/icons/icon-active-96.png +0 -0
  22. package/bundled-relay/public/icons/icon-default-128.png +0 -0
  23. package/bundled-relay/public/icons/icon-default-48.png +0 -0
  24. package/bundled-relay/public/icons/icon-default-96.png +0 -0
  25. package/bundled-relay/public/icons/icon-inactive-128.png +0 -0
  26. package/bundled-relay/public/icons/icon-inactive-48.png +0 -0
  27. package/bundled-relay/public/icons/icon-inactive-96.png +0 -0
  28. package/index.js +1 -1
  29. package/openclaw.plugin.json +0 -12
  30. package/package.json +1 -1
  31. package/skills/bianjie-browser-relay/SKILL.md +562 -0
  32. package/skills/oc-browser-relay-taobao/SKILL.md +0 -213
  33. package/skills/oc-browser-relay-xiaohongshu/SKILL.md +0 -310
  34. package/skills/oc-sycm-live-metrics/SKILL.md +0 -121
@@ -23052,7 +23052,7 @@ var TemplateExecutor = class _TemplateExecutor {
23052
23052
  if (lengthMatch) {
23053
23053
  const [, fieldName, operator, value] = lengthMatch;
23054
23054
  const fieldValue = data[fieldName];
23055
- const length = Array.isArray(fieldValue) ? fieldValue.length : 0;
23055
+ const length = Array.isArray(fieldValue) || typeof fieldValue === "string" ? fieldValue.length : 0;
23056
23056
  const targetValue = parseInt(value);
23057
23057
  switch (operator) {
23058
23058
  case ">=":
@@ -23250,6 +23250,7 @@ function mapToolNameToTaskOperation(tool) {
23250
23250
  case "present_page_activity":
23251
23251
  return "unknown";
23252
23252
  case "invoke_page_request":
23253
+ case "invoke_page_request_group":
23253
23254
  return "extract_api_payload";
23254
23255
  case "collect_search_list":
23255
23256
  case "collect_search_item_details":
@@ -23430,6 +23431,7 @@ function resolveTaskCapability(task, captureCapability) {
23430
23431
  case "save_note_draft":
23431
23432
  case "browser_action":
23432
23433
  case "search_notes":
23434
+ case "wechat_get_article":
23433
23435
  return "browser";
23434
23436
  default:
23435
23437
  return void 0;
@@ -23805,6 +23807,264 @@ function buildTaskFinalResult(task) {
23805
23807
  };
23806
23808
  }
23807
23809
 
23810
+ // processors/taobao/comments.ts
23811
+ function asRecord(value) {
23812
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
23813
+ }
23814
+ function asArray(value) {
23815
+ return Array.isArray(value) ? value : [];
23816
+ }
23817
+ function toNumberOrNull(value) {
23818
+ if (typeof value === "number" && Number.isFinite(value)) {
23819
+ return value;
23820
+ }
23821
+ if (typeof value === "string") {
23822
+ const trimmed = value.trim();
23823
+ if (!trimmed) return null;
23824
+ const parsed = Number(trimmed);
23825
+ return Number.isFinite(parsed) ? parsed : null;
23826
+ }
23827
+ return null;
23828
+ }
23829
+ function toStringOrNull(value) {
23830
+ return typeof value === "string" && value.trim() ? value.trim() : null;
23831
+ }
23832
+ var processTaobaoComments = async (input) => {
23833
+ const targetCount = resolveTargetCommentCount(input.stepArgs?.targetCommentCount);
23834
+ const pages = asArray(input.sources.product_comments_pages).map((page) => asRecord(page)).filter((page) => Boolean(page));
23835
+ const firstPage = pages[0];
23836
+ const firstData = asRecord(firstPage?.data) ?? {};
23837
+ const lastDataWithHasNext = findLastPageDataWithField(pages, "hasNext");
23838
+ const comments = [];
23839
+ const seenIds = /* @__PURE__ */ new Set();
23840
+ for (const page of pages) {
23841
+ const data = asRecord(page.data);
23842
+ const rateList = asArray(data?.rateList);
23843
+ for (const comment of rateList) {
23844
+ const key = toStringOrNull(comment.id) || JSON.stringify(comment);
23845
+ if (seenIds.has(key)) {
23846
+ continue;
23847
+ }
23848
+ seenIds.add(key);
23849
+ comments.push({
23850
+ id: toStringOrNull(comment.id),
23851
+ feedback: toStringOrNull(comment.feedback),
23852
+ feedbackDate: toStringOrNull(comment.feedbackDate),
23853
+ rateType: toStringOrNull(comment.rateType),
23854
+ skuId: toStringOrNull(comment.skuId),
23855
+ skuMap: asRecord(comment.skuMap),
23856
+ decision: comment.decision ?? null,
23857
+ interactInfo: {
23858
+ likeCount: toNumberOrNull(asRecord(comment.interactInfo)?.likeCount),
23859
+ commentCount: toNumberOrNull(asRecord(comment.interactInfo)?.commentCount),
23860
+ readCount: toNumberOrNull(asRecord(comment.interactInfo)?.readCount)
23861
+ },
23862
+ pictures: asArray(comment.feedPicPathList).map((url) => ({
23863
+ url: toStringOrNull(url)
23864
+ })).filter((entry) => entry.url),
23865
+ userNick: toStringOrNull(comment.userNick),
23866
+ creditLevel: toStringOrNull(comment.creditLevel)
23867
+ });
23868
+ if (targetCount > 0 && comments.length >= targetCount) {
23869
+ break;
23870
+ }
23871
+ }
23872
+ if (targetCount > 0 && comments.length >= targetCount) {
23873
+ break;
23874
+ }
23875
+ }
23876
+ return {
23877
+ data: {
23878
+ feedAllCount: toNumberOrNull(firstData.feedAllCount),
23879
+ feedGoodCount: toNumberOrNull(firstData.feedGoodCount),
23880
+ feedBadCount: toNumberOrNull(firstData.feedBadCount),
23881
+ feedNormalCount: toNumberOrNull(firstData.feedNormalCount),
23882
+ feedMediaCount: toNumberOrNull(firstData.feedMediaCount),
23883
+ feedVideoCount: toNumberOrNull(firstData.feedVideoCount),
23884
+ hasNext: lastDataWithHasNext?.hasNext ?? firstData.hasNext ?? null,
23885
+ total: toNumberOrNull(firstData.total),
23886
+ totalPage: toNumberOrNull(firstData.totalPage),
23887
+ timePeriodDesc: toStringOrNull(firstData.timePeriodDesc),
23888
+ comments
23889
+ },
23890
+ meta: {
23891
+ pageCount: pages.length,
23892
+ commentCount: comments.length,
23893
+ targetCount: targetCount > 0 ? targetCount : null
23894
+ }
23895
+ };
23896
+ };
23897
+ function resolveTargetCommentCount(value) {
23898
+ const parsed = Number(value);
23899
+ if (!Number.isFinite(parsed) || parsed <= 0) {
23900
+ return 0;
23901
+ }
23902
+ return Math.floor(parsed);
23903
+ }
23904
+ function findLastPageDataWithField(pages, key) {
23905
+ for (let index = pages.length - 1; index >= 0; index -= 1) {
23906
+ const data = asRecord(pages[index]?.data);
23907
+ if (data && data[key] !== void 0) {
23908
+ return data;
23909
+ }
23910
+ }
23911
+ return null;
23912
+ }
23913
+
23914
+ // processors/taobao/ask-answers.ts
23915
+ function asRecord2(value) {
23916
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
23917
+ }
23918
+ function asArray2(value) {
23919
+ return Array.isArray(value) ? value : [];
23920
+ }
23921
+ function toNumberOrNull2(value) {
23922
+ if (typeof value === "number" && Number.isFinite(value)) {
23923
+ return value;
23924
+ }
23925
+ if (typeof value === "string") {
23926
+ const trimmed = value.trim();
23927
+ if (!trimmed) return null;
23928
+ const parsed = Number(trimmed);
23929
+ return Number.isFinite(parsed) ? parsed : null;
23930
+ }
23931
+ return null;
23932
+ }
23933
+ function toStringOrNull2(value) {
23934
+ return typeof value === "string" && value.trim() ? value.trim() : null;
23935
+ }
23936
+ var processTaobaoAskAnswers = async (input) => {
23937
+ const targetCount = resolveTargetAskAnswerCount(input.stepArgs?.targetAskAnswerCount);
23938
+ const pages = asArray2(input.sources.ask_answers_pages);
23939
+ const normalizedPages = pages.map((page) => asRecord2(page)).filter((page) => Boolean(page));
23940
+ const firstPage = normalizedPages[0];
23941
+ const firstData = asRecord2(firstPage?.data) ?? {};
23942
+ const questions = [];
23943
+ const seenQuestionKeys = /* @__PURE__ */ new Set();
23944
+ for (const page of normalizedPages) {
23945
+ const data = asRecord2(page.data);
23946
+ const questionList = asArray2(data?.questionList);
23947
+ for (const question of questionList) {
23948
+ const key = toStringOrNull2(question.questionId) || toStringOrNull2(question.id) || toStringOrNull2(question.questionTitle) || JSON.stringify(question);
23949
+ if (seenQuestionKeys.has(key)) {
23950
+ continue;
23951
+ }
23952
+ seenQuestionKeys.add(key);
23953
+ questions.push({
23954
+ questionId: toStringOrNull2(question.questionId) || toStringOrNull2(question.id),
23955
+ questionTitle: toStringOrNull2(question.questionTitle),
23956
+ answerCount: toNumberOrNull2(question.answerCount),
23957
+ features: asArray2(question.features),
23958
+ topAnswerList: asArray2(question.topAnswerList)
23959
+ });
23960
+ if (targetCount > 0 && questions.length >= targetCount) {
23961
+ break;
23962
+ }
23963
+ }
23964
+ if (targetCount > 0 && questions.length >= targetCount) {
23965
+ break;
23966
+ }
23967
+ }
23968
+ const tags = asArray2(firstData.tags).map((tag) => ({
23969
+ keyword: toStringOrNull2(tag.keyword),
23970
+ count: toNumberOrNull2(tag.count)
23971
+ }));
23972
+ return {
23973
+ data: {
23974
+ questionTotal: toNumberOrNull2(firstData.questionTotal),
23975
+ hasNext: firstData.hasNext,
23976
+ tags,
23977
+ questions
23978
+ },
23979
+ meta: {
23980
+ pageCount: normalizedPages.length,
23981
+ questionCount: questions.length,
23982
+ targetCount: targetCount > 0 ? targetCount : null
23983
+ }
23984
+ };
23985
+ };
23986
+ function resolveTargetAskAnswerCount(value) {
23987
+ const parsed = Number(value);
23988
+ if (!Number.isFinite(parsed) || parsed <= 0) {
23989
+ return 0;
23990
+ }
23991
+ return Math.floor(parsed);
23992
+ }
23993
+
23994
+ // processors/registry.ts
23995
+ var PROCESSORS = {
23996
+ "taobao.comments": processTaobaoComments,
23997
+ "taobao.ask_answers": processTaobaoAskAnswers
23998
+ };
23999
+ function resolveStructuredDataProcessor(processorId) {
24000
+ return PROCESSORS[processorId] ?? null;
24001
+ }
24002
+
24003
+ // relay/src/structured-data-processor.ts
24004
+ function isProcessorEnvelope(value) {
24005
+ return Boolean(
24006
+ value && typeof value === "object" && value.mode === "relay_processor" && typeof value.processorId === "string" && value.processorId.length > 0 && typeof value.input === "object"
24007
+ );
24008
+ }
24009
+ async function applyStructuredDataProcessorToNodeResult(result) {
24010
+ if (!result.ok || result.tool !== "collect_structured_data") {
24011
+ return result;
24012
+ }
24013
+ const processing = result.observation?.processing;
24014
+ if (!isProcessorEnvelope(processing)) {
24015
+ return result;
24016
+ }
24017
+ const processor = resolveStructuredDataProcessor(processing.processorId);
24018
+ if (!processor) {
24019
+ return {
24020
+ ...result,
24021
+ ok: false,
24022
+ status: "failed",
24023
+ observation: {
24024
+ extractor: processing.input.extractor,
24025
+ processorId: processing.processorId
24026
+ },
24027
+ error: {
24028
+ code: ERROR_CODES.ERR_STEP_EXECUTION_FAILED,
24029
+ message: `Structured data processor not found: ${processing.processorId}`
24030
+ }
24031
+ };
24032
+ }
24033
+ try {
24034
+ const output = await processor(processing.input);
24035
+ return {
24036
+ ...result,
24037
+ observation: {
24038
+ ...result.observation,
24039
+ data: output.data,
24040
+ meta: {
24041
+ ...result.observation.meta ?? {},
24042
+ processor: {
24043
+ id: processing.processorId,
24044
+ appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
24045
+ ...output.meta ? { meta: output.meta } : {}
24046
+ }
24047
+ },
24048
+ processing: void 0
24049
+ }
24050
+ };
24051
+ } catch (error) {
24052
+ return {
24053
+ ...result,
24054
+ ok: false,
24055
+ status: "failed",
24056
+ observation: {
24057
+ extractor: processing.input.extractor,
24058
+ processorId: processing.processorId
24059
+ },
24060
+ error: {
24061
+ code: ERROR_CODES.ERR_STEP_EXECUTION_FAILED,
24062
+ message: error instanceof Error ? error.message : `Processor failed: ${processing.processorId}`
24063
+ }
24064
+ };
24065
+ }
24066
+ }
24067
+
23808
24068
  // shared/node-template-compiler.ts
23809
24069
  function deriveOnFailurePolicyFromRetry(node) {
23810
24070
  if (node.onFailure) {
@@ -24139,7 +24399,7 @@ app.use((req, res, next) => {
24139
24399
  }
24140
24400
  next();
24141
24401
  });
24142
- app.use(import_express.default.json());
24402
+ app.use(import_express.default.json({ limit: "100mb" }));
24143
24403
  function logRelayFlow(message, details) {
24144
24404
  if (details) {
24145
24405
  console.log(`[Relay][Flow] ${message}`, details);
@@ -24147,6 +24407,37 @@ function logRelayFlow(message, details) {
24147
24407
  }
24148
24408
  console.log(`[Relay][Flow] ${message}`);
24149
24409
  }
24410
+ function previewRelayText(value, maxLength = 160) {
24411
+ if (value.length <= maxLength) {
24412
+ return value;
24413
+ }
24414
+ return `${value.slice(0, maxLength)}...`;
24415
+ }
24416
+ function buildRelayTaskPayloadSummary(payload) {
24417
+ const data = payload || {};
24418
+ const payloadKeys = Object.keys(data).filter((key) => key !== "nodes" && key !== "nodeResults");
24419
+ const contentValue = data.content;
24420
+ const titleValue = data.title;
24421
+ const topicsValue = data.topics;
24422
+ const imagesValue = data.images;
24423
+ const imageRefsValue = data.imageRefs;
24424
+ const videoValue = data.video;
24425
+ return {
24426
+ payloadKeys,
24427
+ hasTitle: typeof titleValue === "string" && titleValue.length > 0,
24428
+ titleLength: typeof titleValue === "string" ? titleValue.length : 0,
24429
+ titlePreview: typeof titleValue === "string" ? previewRelayText(titleValue, 80) : void 0,
24430
+ hasContentField: Object.prototype.hasOwnProperty.call(data, "content"),
24431
+ contentType: contentValue == null ? typeof contentValue : Array.isArray(contentValue) ? "array" : typeof contentValue,
24432
+ contentLength: typeof contentValue === "string" ? contentValue.length : 0,
24433
+ contentPreview: typeof contentValue === "string" ? previewRelayText(contentValue) : void 0,
24434
+ topicCount: Array.isArray(topicsValue) ? topicsValue.length : 0,
24435
+ imageCount: Array.isArray(imagesValue) ? imagesValue.length : 0,
24436
+ imageRefCount: Array.isArray(imageRefsValue) ? imageRefsValue.length : 0,
24437
+ hasVideoField: typeof videoValue === "string" && videoValue.length > 0,
24438
+ videoPreview: typeof videoValue === "string" ? previewRelayText(videoValue, 120) : void 0
24439
+ };
24440
+ }
24150
24441
  function createRelayError(message, code = ERROR_CODES.ERR_INVALID_TASK, status = 400) {
24151
24442
  const error = new Error(message);
24152
24443
  error.code = code;
@@ -24558,7 +24849,8 @@ async function dispatchTask(task) {
24558
24849
  siteId: task.context?.siteId,
24559
24850
  batchJobId: task.context?.batchJobId,
24560
24851
  batchItemIndex: task.context?.batchItemIndex,
24561
- nodeTools: task.payload?.nodes?.map((node) => `${node.nodeId}:${node.tool}`) || []
24852
+ nodeTools: task.payload?.nodes?.map((node) => `${node.nodeId}:${node.tool}`) || [],
24853
+ inputSummary: buildRelayTaskPayloadSummary(task.payload)
24562
24854
  });
24563
24855
  const taskMessage = {
24564
24856
  type: "task.submit",
@@ -24795,7 +25087,7 @@ async function handleRuntimeMessage(msg) {
24795
25087
  handleArtifactCreated(msg);
24796
25088
  break;
24797
25089
  case "node.result":
24798
- handleNodeResult(msg);
25090
+ void handleNodeResult(msg);
24799
25091
  break;
24800
25092
  case "runtime.query.result":
24801
25093
  handleRuntimeQueryResult(msg);
@@ -24904,8 +25196,9 @@ function handleStepProgress(msg) {
24904
25196
  }
24905
25197
  applyStepProgressToTask(task, payload);
24906
25198
  }
24907
- function handleNodeResult(msg) {
24908
- const rawResult = msg.payload;
25199
+ async function handleNodeResult(msg) {
25200
+ const initialResult = msg.payload;
25201
+ const rawResult = await applyStructuredDataProcessorToNodeResult(initialResult);
24909
25202
  logRelayFlow("Received node result", {
24910
25203
  taskId: rawResult.taskId,
24911
25204
  nodeId: rawResult.nodeId,
@@ -25431,7 +25724,9 @@ app.get("/api/tools", async (req, res) => {
25431
25724
  });
25432
25725
  app.post("/api/tools/:toolName/execute", async (req, res) => {
25433
25726
  const { toolName } = req.params;
25727
+ console.log(`\u{1F680} ~ index.ts:1697 ~ toolName:`, toolName);
25434
25728
  const { input = {}, runtimeId } = req.body || {};
25729
+ console.log(`\u{1F680} ~ index.ts:1698 ~ req.body:`, req.body);
25435
25730
  const effectiveRuntimeId = resolveRuntimeIdOrDefault(runtimeId);
25436
25731
  try {
25437
25732
  if (toolName === "list_runtimes") {