oc-browser-relay 1.0.18 → 1.0.20

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.
@@ -18420,7 +18420,7 @@ var require_view = __commonJS({
18420
18420
  var dirname2 = path4.dirname;
18421
18421
  var basename4 = path4.basename;
18422
18422
  var extname2 = path4.extname;
18423
- var join2 = path4.join;
18423
+ var join3 = path4.join;
18424
18424
  var resolve4 = path4.resolve;
18425
18425
  module.exports = View;
18426
18426
  function View(name, options) {
@@ -18468,12 +18468,12 @@ var require_view = __commonJS({
18468
18468
  };
18469
18469
  View.prototype.resolve = function resolve5(dir, file) {
18470
18470
  var ext = this.ext;
18471
- var path5 = join2(dir, file);
18471
+ var path5 = join3(dir, file);
18472
18472
  var stat = tryStat(path5);
18473
18473
  if (stat && stat.isFile()) {
18474
18474
  return path5;
18475
18475
  }
18476
- path5 = join2(dir, basename4(file, ext), "index" + ext);
18476
+ path5 = join3(dir, basename4(file, ext), "index" + ext);
18477
18477
  stat = tryStat(path5);
18478
18478
  if (stat && stat.isFile()) {
18479
18479
  return path5;
@@ -19109,7 +19109,7 @@ var require_send = __commonJS({
19109
19109
  var Stream = __require("stream");
19110
19110
  var util = __require("util");
19111
19111
  var extname2 = path4.extname;
19112
- var join2 = path4.join;
19112
+ var join3 = path4.join;
19113
19113
  var normalize = path4.normalize;
19114
19114
  var resolve4 = path4.resolve;
19115
19115
  var sep = path4.sep;
@@ -19328,7 +19328,7 @@ var require_send = __commonJS({
19328
19328
  return res;
19329
19329
  }
19330
19330
  parts = path5.split(sep);
19331
- path5 = normalize(join2(root, path5));
19331
+ path5 = normalize(join3(root, path5));
19332
19332
  } else {
19333
19333
  if (UP_PATH_REGEXP.test(path5)) {
19334
19334
  debug('malicious path "%s"', path5);
@@ -19463,7 +19463,7 @@ var require_send = __commonJS({
19463
19463
  if (err) return self.onStatError(err);
19464
19464
  return self.error(404);
19465
19465
  }
19466
- var p = join2(path5, self._index[i]);
19466
+ var p = join3(path5, self._index[i]);
19467
19467
  debug('stat "%s"', p);
19468
19468
  fs4.stat(p, function(err2, stat) {
19469
19469
  if (err2) return next(err2);
@@ -22610,6 +22610,7 @@ import { fileURLToPath } from "url";
22610
22610
 
22611
22611
  // relay/src/file-service/index.ts
22612
22612
  import * as fs from "fs";
22613
+ import * as os from "os";
22613
22614
  import * as path from "path";
22614
22615
  import * as crypto from "crypto";
22615
22616
 
@@ -22655,10 +22656,12 @@ var RELAY_HTTP_URL = `http://localhost:${DEFAULT_RELAY_PORT}`;
22655
22656
 
22656
22657
  // relay/src/file-service/index.ts
22657
22658
  var FileService = class {
22658
- constructor(allowedDirs = []) {
22659
+ constructor(allowedDirs = [], storageDir = process.env.RELAY_FILE_STORAGE_DIR ? path.resolve(process.env.RELAY_FILE_STORAGE_DIR) : path.join(os.tmpdir(), "chromeagent-relay-files")) {
22659
22660
  this.fileMap = /* @__PURE__ */ new Map();
22660
22661
  this.allowedDirs = [];
22661
22662
  this.allowedDirs = allowedDirs;
22663
+ this.storageDir = storageDir;
22664
+ fs.mkdirSync(this.storageDir, { recursive: true });
22662
22665
  }
22663
22666
  async stat(filePath) {
22664
22667
  if (!this.isAllowed(filePath)) {
@@ -22689,6 +22692,22 @@ var FileService = class {
22689
22692
  this.fileMap.set(fileId, prepared);
22690
22693
  return prepared;
22691
22694
  }
22695
+ async storeBase64(input) {
22696
+ const fileId = `file_${crypto.randomBytes(4).toString("hex")}`;
22697
+ const normalizedName = this.normalizeFileName(input.name, input.mimeType);
22698
+ const targetPath = path.join(this.storageDir, `${fileId}_${normalizedName}`);
22699
+ const content = Buffer.from(input.contentBase64, "base64");
22700
+ fs.writeFileSync(targetPath, content);
22701
+ const prepared = {
22702
+ fileId,
22703
+ name: normalizedName,
22704
+ mimeType: input.mimeType || this.getMimeType(targetPath),
22705
+ size: content.byteLength,
22706
+ path: targetPath
22707
+ };
22708
+ this.fileMap.set(fileId, prepared);
22709
+ return prepared;
22710
+ }
22692
22711
  getFile(fileId) {
22693
22712
  return this.fileMap.get(fileId);
22694
22713
  }
@@ -22721,10 +22740,49 @@ var FileService = class {
22721
22740
  return "image/png";
22722
22741
  case ".mp4":
22723
22742
  return "video/mp4";
22743
+ case ".zip":
22744
+ return "application/zip";
22745
+ case ".xlsx":
22746
+ return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
22747
+ case ".json":
22748
+ return "application/json";
22749
+ case ".txt":
22750
+ return "text/plain; charset=utf-8";
22724
22751
  default:
22725
22752
  return "application/octet-stream";
22726
22753
  }
22727
22754
  }
22755
+ normalizeFileName(name, mimeType) {
22756
+ const fallbackExt = this.getExtensionForMimeType(mimeType);
22757
+ const rawName = typeof name === "string" && name.trim() ? name.trim() : `download${fallbackExt}`;
22758
+ const sanitized = rawName.replace(/[<>:"/\\|?*\u0000-\u001f]/g, "_").replace(/\s+/g, " ").trim();
22759
+ if (!path.extname(sanitized) && fallbackExt) {
22760
+ return `${sanitized}${fallbackExt}`;
22761
+ }
22762
+ return sanitized || `download${fallbackExt}`;
22763
+ }
22764
+ getExtensionForMimeType(mimeType) {
22765
+ switch ((mimeType || "").toLowerCase()) {
22766
+ case "application/zip":
22767
+ case "application/x-zip-compressed":
22768
+ return ".zip";
22769
+ case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
22770
+ return ".xlsx";
22771
+ case "application/json":
22772
+ return ".json";
22773
+ case "text/plain":
22774
+ case "text/plain; charset=utf-8":
22775
+ return ".txt";
22776
+ case "image/jpeg":
22777
+ return ".jpg";
22778
+ case "image/png":
22779
+ return ".png";
22780
+ case "video/mp4":
22781
+ return ".mp4";
22782
+ default:
22783
+ return ".bin";
22784
+ }
22785
+ }
22728
22786
  };
22729
22787
 
22730
22788
  // relay/src/workspace-service/index.ts
@@ -23120,8 +23178,18 @@ var TemplateExecutor = class _TemplateExecutor {
23120
23178
  }
23121
23179
  }
23122
23180
  this.applySycmLiveMetricDerivedDefaults(template, data, hydrated);
23181
+ this.applyAlimamaKeywordReportDerivedDefaults(template, data, hydrated);
23123
23182
  return hydrated;
23124
23183
  }
23184
+ applyAlimamaKeywordReportDerivedDefaults(template, rawInput, hydrated) {
23185
+ const isAlimamaReportDownloadTemplate = template.siteId === "alimama_report" && ["download_keyword_report", "download_crowd_report"].includes(template.taskType);
23186
+ if (!isAlimamaReportDownloadTemplate) {
23187
+ return;
23188
+ }
23189
+ if (rawInput.vsTime === void 0 && typeof hydrated.endTime === "string" && hydrated.endTime.trim()) {
23190
+ hydrated.vsTime = hydrated.endTime.trim();
23191
+ }
23192
+ }
23125
23193
  applySycmLiveMetricDerivedDefaults(template, rawInput, hydrated) {
23126
23194
  const isSycmLiveMetricsTemplate = template.siteId === "sycm_taobao" && template.taskType === "browser_action" && (template.optionalFields ?? []).some((field) => field.name === "metricPeriodLabel");
23127
23195
  if (!isSycmLiveMetricsTemplate) {
@@ -23324,6 +23392,7 @@ function mapToolNameToTaskOperation(tool) {
23324
23392
  switch (tool) {
23325
23393
  case "open_tab":
23326
23394
  case "close_tab":
23395
+ case "download_page_file":
23327
23396
  case "ensure_publish_ready":
23328
23397
  return "navigate";
23329
23398
  case "fill_field":
@@ -25992,6 +26061,34 @@ app.post("/api/file/stat", async (req, res) => {
25992
26061
  res.status(status).json({ ok: false, error: { code, message: err.message } });
25993
26062
  }
25994
26063
  });
26064
+ app.post("/api/file/store", async (req, res) => {
26065
+ try {
26066
+ const { name, mimeType, contentBase64 } = req.body || {};
26067
+ if (typeof contentBase64 !== "string" || contentBase64.length === 0) {
26068
+ return res.status(400).json({
26069
+ ok: false,
26070
+ error: {
26071
+ code: ERROR_CODES.ERR_INVALID_TASK,
26072
+ message: "contentBase64 is required"
26073
+ }
26074
+ });
26075
+ }
26076
+ const file = await fileService.storeBase64({
26077
+ name: typeof name === "string" ? name : void 0,
26078
+ mimeType: typeof mimeType === "string" ? mimeType : void 0,
26079
+ contentBase64
26080
+ });
26081
+ return res.json({ ok: true, file });
26082
+ } catch (err) {
26083
+ return res.status(500).json({
26084
+ ok: false,
26085
+ error: {
26086
+ code: ERROR_CODES.ERR_FILE_PREPARE_FAILED,
26087
+ message: err.message
26088
+ }
26089
+ });
26090
+ }
26091
+ });
25995
26092
  app.get("/api/file/content/:fileId", async (req, res) => {
25996
26093
  try {
25997
26094
  const { fileId } = req.params;
@@ -26192,7 +26289,7 @@ function continueOrchestrator(taskId, completedNodeId, nextStepIndex) {
26192
26289
  return;
26193
26290
  }
26194
26291
  advanceTaskOrchestrator(orch, nextStepIndex);
26195
- console.log(`[Relay] \u8282\u70B9\u7D22\u5F15\u9012\u589E\u81F3: ${orch.currentStepIndex}`);
26292
+ console.log(`[Relay] \u8282\u70B9\u7D22\u5F15\u9012\u589E\u81F3: ${orch.currentStepIndex},${(/* @__PURE__ */ new Date()).toISOString()}`);
26196
26293
  setTimeout(() => {
26197
26294
  void sendNextStep(taskId);
26198
26295
  }, STEP_DISPATCH_INTERVAL_MS);
@@ -0,0 +1,204 @@
1
+ {
2
+ "pageId": "alimama_account_report",
3
+ "pageName": "阿里妈妈账户报表页",
4
+ "platform": "taobao",
5
+ "siteId": "alimama_report",
6
+ "pageType": "dashboard_home",
7
+ "version": "1.0.0",
8
+ "urlMatch": [
9
+ "https://one.alimama.com/index.html#!/report/account*"
10
+ ],
11
+ "readiness": {
12
+ "loginRequired": true,
13
+ "pageReadyWhen": {
14
+ "type": "url_contains",
15
+ "value": "/report/account"
16
+ },
17
+ "waitTimeoutMs": 15000
18
+ },
19
+ "captureSources": {
20
+ "auth_info": {
21
+ "urlPattern": "/member/checkAccess.json",
22
+ "method": "POST",
23
+ "capability": "hybrid",
24
+ "operation": "capture_response",
25
+ "persistent": true,
26
+ "artifactType": "hybrid",
27
+ "includeBody": true,
28
+ "locationSuffix": "alimama_report_auth_info",
29
+ "hint": "capture alimama checkAccess auth info"
30
+ }
31
+ },
32
+ "capturePlans": {
33
+ "auth_info": {
34
+ "sourceId": "auth_info",
35
+ "captureNetwork": {
36
+ "timeoutMs": 20000,
37
+ "startPhase": "before_action",
38
+ "actionScope": "step"
39
+ }
40
+ }
41
+ },
42
+ "apiSources": {
43
+ "create_download_task": {
44
+ "method": "POST",
45
+ "url": "https://one.alimama.com/report/createDownLoadTask.json",
46
+ "query": {
47
+ "csrfId": "{{payload.csrfId}}",
48
+ "bizCode": "universalBP"
49
+ },
50
+ "headers": {
51
+ "Content-Type": "application/json"
52
+ },
53
+ "body": {
54
+ "startTime": "{{payload.startTime}}",
55
+ "endTime": "{{payload.endTime}}",
56
+ "vsTime": "{{payload.vsTime}}",
57
+ "vsType": "{{payload.vsType}}",
58
+ "searchValue": "{{payload.searchValue}}",
59
+ "csrfId": "{{payload.csrfId}}",
60
+ "loginPointId": "{{payload.loginPointId}}",
61
+ "excelName": "{{payload.excelName}}",
62
+ "bizCode": "universalBP",
63
+ "bizCodeIn": [
64
+ "onebpSearch"
65
+ ],
66
+ "byPage": false,
67
+ "effectEqual": 15,
68
+ "fieldType": "all",
69
+ "from": "pcBaseReport",
70
+ "fromRealTime": false,
71
+ "havingList": [],
72
+ "isKeyWordNotContainChase": "true",
73
+ "offset": 0,
74
+ "pageSize": 60,
75
+ "parentAdcName": "report_frame_bidword",
76
+ "queryDomains": [
77
+ "word",
78
+ "date",
79
+ "campaign",
80
+ "adgroup"
81
+ ],
82
+ "queryFieldIn": [
83
+ "adPv",
84
+ "click",
85
+ "charge",
86
+ "ctr",
87
+ "ecpc",
88
+ "alipayInshopAmt",
89
+ "alipayInshopNum",
90
+ "cvr"
91
+ ],
92
+ "rptType": "bidword",
93
+ "searchKey": "strategyBidwordNameLike",
94
+ "source": "async_dowdload",
95
+ "splitType": "{{payload.splitType}}",
96
+ "unifyType": "zhai"
97
+ },
98
+ "responsePath": "data.taskId",
99
+ "metaPath": "data"
100
+ },
101
+ "create_crowd_download_task": {
102
+ "method": "POST",
103
+ "url": "https://one.alimama.com/report/createDownLoadTask.json",
104
+ "query": {
105
+ "csrfId": "{{payload.csrfId}}",
106
+ "bizCode": "universalBP"
107
+ },
108
+ "headers": {
109
+ "Content-Type": "application/json"
110
+ },
111
+ "body": {
112
+ "excelName": "{{payload.excelName}}",
113
+ "pageSize": 60,
114
+ "offset": 0,
115
+ "havingList": [],
116
+ "endTime": "{{payload.endTime}}",
117
+ "from": "pcBaseReport",
118
+ "bizCodeIn": [
119
+ "onebpSearch"
120
+ ],
121
+ "unifyType": "zhai",
122
+ "effectEqual": 15,
123
+ "startTime": "{{payload.startTime}}",
124
+ "filterAppendSubwayChannel": true,
125
+ "filterNullCrowdSubwayTag": true,
126
+ "queryFieldIn": [
127
+ "adPv",
128
+ "click",
129
+ "charge",
130
+ "ctr",
131
+ "ecpc",
132
+ "alipayInshopAmt",
133
+ "alipayInshopNum",
134
+ "cvr",
135
+ "cartInshopNum",
136
+ "itemColInshopNum",
137
+ "shopColDirNum",
138
+ "colNum",
139
+ "itemColInshopCost"
140
+ ],
141
+ "vsType": "{{payload.vsType}}",
142
+ "vsTime": "{{payload.vsTime}}",
143
+ "searchValue": "{{payload.searchValue}}",
144
+ "searchKey": "strategyTargetTitleLike",
145
+ "queryDomains": [
146
+ "crowd",
147
+ "promotion",
148
+ "date",
149
+ "campaign",
150
+ "adgroup"
151
+ ],
152
+ "fieldType": "all",
153
+ "rptType": "crowd",
154
+ "parentAdcName": "report_frame_crowd",
155
+ "byPage": false,
156
+ "fromRealTime": false,
157
+ "source": "async_dowdload",
158
+ "splitType": "{{payload.splitType}}",
159
+ "csrfId": "{{payload.csrfId}}",
160
+ "bizCode": "universalBP",
161
+ "loginPointId": "{{payload.loginPointId}}"
162
+ },
163
+ "responsePath": "data.taskId",
164
+ "metaPath": "data"
165
+ },
166
+ "get_download_url": {
167
+ "method": "GET",
168
+ "url": "https://bpcommon.alimama.com/commonapi/report/async/getDownloadUrl.json",
169
+ "query": {
170
+ "taskId": "{{payload.taskId}}",
171
+ "bizCode": "universalBP",
172
+ "csrfId": "{{payload.csrfId}}",
173
+ "loginPointId": "{{payload.loginPointId}}"
174
+ },
175
+ "responsePath": "data.result.downloadUrl",
176
+ "metaPath": "data.result"
177
+ }
178
+ },
179
+ "crawlerHints": {
180
+ "structuredDataExtractors": {
181
+ "download_auth_info": {
182
+ "adapterId": "alimama_report_auth_capture",
183
+ "sourceId": "alimama_report_auth_info",
184
+ "timeoutMs": 20000,
185
+ "observationSources": [
186
+ {
187
+ "sourceId": "alimama_report_auth_capture",
188
+ "nodeIdArg": "sourceNodeId",
189
+ "sourceType": "active_capture_results",
190
+ "consumeCapture": true,
191
+ "resultPath": "$self"
192
+ }
193
+ ],
194
+ "outputSchema": {
195
+ "root": {
196
+ "csrfId": "sources.alimama_report_auth_info.csrfId",
197
+ "loginPointId": "sources.alimama_report_auth_info.loginPointId",
198
+ "meta": "sources.alimama_report_auth_info.meta"
199
+ }
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "files": [
3
+ "alimama-account-report.json",
3
4
  "sycm-taobao-home.json",
4
5
  "xiaohongshu-creator-publish.json",
5
6
  "xiaohongshu-web-explore.json",
@@ -0,0 +1,12 @@
1
+ {
2
+ "siteId": "alimama_report",
3
+ "siteName": "阿里妈妈报表中心",
4
+ "platform": "taobao",
5
+ "hosts": [
6
+ "one.alimama.com"
7
+ ],
8
+ "loginPagePatterns": [
9
+ "https://login.taobao.com/*",
10
+ "https://login.alimama.com/*"
11
+ ]
12
+ }
@@ -23,6 +23,7 @@
23
23
  { "kind": "xpath", "value": "//div[.//div[normalize-space(text())='发布视频'] and .//div[contains(normalize-space(.), '推荐mp4、webm')]]", "priority": 2 },
24
24
  { "kind": "css", "value": "a[href*='/content/upload']", "priority": 3 }
25
25
  ],
26
+ "preClickDelayMs": 5000,
26
27
  "readyTimeoutMs": 8000
27
28
  }
28
29
  }
@@ -3,6 +3,7 @@
3
3
  "xiaohongshu-creator.json",
4
4
  "xiaohongshu-web.json",
5
5
  "taobao-web.json",
6
+ "alimama-report.json",
6
7
  "sycm-taobao.json",
7
8
  "douyin-creator.json",
8
9
  "douyin-fxg.json",
@@ -0,0 +1,166 @@
1
+ {
2
+ "siteId": "alimama_report",
3
+ "taskType": "download_crowd_report",
4
+ "platform": "taobao",
5
+ "name": "下载阿里妈妈人群报表",
6
+ "description": "打开阿里妈妈账户报表页,捕获鉴权信息,创建人群报表异步下载任务,轮询下载链接并将文件回传 relay 存储。",
7
+ "requiredFields": [
8
+ {
9
+ "name": "startTime",
10
+ "type": "string",
11
+ "description": "报表开始日期,格式 YYYY-MM-DD"
12
+ },
13
+ {
14
+ "name": "endTime",
15
+ "type": "string",
16
+ "description": "报表结束日期,格式 YYYY-MM-DD"
17
+ }
18
+ ],
19
+ "optionalFields": [
20
+ {
21
+ "name": "splitType",
22
+ "type": "string",
23
+ "description": "报表汇总维度,支持 sum/day/week/month/calendarmonth",
24
+ "default": "sum"
25
+ },
26
+ {
27
+ "name": "returnHostPatterns",
28
+ "type": "array",
29
+ "description": "任务结束后若存在匹配这些 host 或 URL 片段的标签页,则切回该标签页"
30
+ }
31
+ ],
32
+ "nodes": [
33
+ {
34
+ "nodeId": "s1",
35
+ "name": "open_alimama_report",
36
+ "goal": "打开阿里妈妈账户报表页并预抓鉴权接口",
37
+ "tool": "open_tab",
38
+ "args": {
39
+ "url": "https://one.alimama.com/index.html#!/report/account?rptType=account",
40
+ "captureNetwork": {
41
+ "capturePlanId": "auth_info"
42
+ }
43
+ }
44
+ },
45
+ {
46
+ "nodeId": "s2",
47
+ "name": "validate_alimama_report",
48
+ "goal": "校验当前页面已进入账户报表页",
49
+ "tool": "validate_page",
50
+ "args": {}
51
+ },
52
+ {
53
+ "nodeId": "s3",
54
+ "name": "collect_auth_info",
55
+ "goal": "从 checkAccess 响应中提取 csrfId 和 loginPointId",
56
+ "tool": "collect_structured_data",
57
+ "args": {
58
+ "extractor": "download_auth_info",
59
+ "sourceNodeId": "s1"
60
+ },
61
+ "storeAs": "authInfo"
62
+ },
63
+ {
64
+ "nodeId": "s4",
65
+ "name": "show_download_activity",
66
+ "goal": "在页面顶部展示独立提示,标记当前正在准备人群报表下载",
67
+ "tool": "present_page_activity",
68
+ "args": {
69
+ "mode": "start",
70
+ "message": "正在提交报表下载任务",
71
+ "style": "info",
72
+ "profile": "none"
73
+ }
74
+ },
75
+ {
76
+ "nodeId": "s5",
77
+ "name": "create_crowd_download_task",
78
+ "goal": "创建人群报表异步下载任务",
79
+ "tool": "invoke_page_request",
80
+ "args": {
81
+ "sourceId": "create_crowd_download_task",
82
+ "payload": {
83
+ "startTime": "{{data.startTime}}",
84
+ "endTime": "{{data.endTime}}",
85
+ "vsTime": "{{data.endTime}}",
86
+ "vsType": "week",
87
+ "searchValue": "",
88
+ "splitType": "{{data.splitType}}",
89
+ "excelName": "人群报表_{{data.startTime}}_{{data.endTime}}"
90
+ }
91
+ },
92
+ "fromContext": {
93
+ "payload.csrfId": "outputs.authInfo.data.csrfId",
94
+ "payload.loginPointId": "outputs.authInfo.data.loginPointId"
95
+ },
96
+ "storeAs": "downloadTask"
97
+ },
98
+ {
99
+ "nodeId": "s6",
100
+ "name": "update_download_activity",
101
+ "goal": "更新页面提示为等待下载链接",
102
+ "tool": "present_page_activity",
103
+ "args": {
104
+ "mode": "start",
105
+ "message": "正在等待报表生成完成",
106
+ "style": "info",
107
+ "profile": "none"
108
+ }
109
+ },
110
+ {
111
+ "nodeId": "s7",
112
+ "name": "poll_download_url",
113
+ "goal": "等待下载任务生成链接并轮询拿到下载地址",
114
+ "tool": "invoke_page_request",
115
+ "args": {
116
+ "sourceId": "get_download_url",
117
+ "payload": {},
118
+ "polling": {
119
+ "initialDelayMs": 30000,
120
+ "intervalMs": 5000,
121
+ "timeoutMs": 120000,
122
+ "maxAttempts": 25
123
+ }
124
+ },
125
+ "fromContext": {
126
+ "payload.taskId": "outputs.downloadTask.response.data",
127
+ "payload.csrfId": "outputs.authInfo.data.csrfId",
128
+ "payload.loginPointId": "outputs.authInfo.data.loginPointId"
129
+ },
130
+ "storeAs": "downloadLink"
131
+ },
132
+ {
133
+ "nodeId": "s8",
134
+ "name": "download_report_file",
135
+ "goal": "拉取下载链接并把文件内容回传给 relay 存储",
136
+ "tool": "download_page_file",
137
+ "args": {
138
+ "filename": "人群报表_{{data.startTime}}_{{data.endTime}}.zip"
139
+ },
140
+ "fromContext": {
141
+ "url": "outputs.downloadLink.response.data"
142
+ },
143
+ "storeAs": "downloadResult"
144
+ },
145
+ {
146
+ "nodeId": "s9",
147
+ "name": "hide_download_activity",
148
+ "goal": "在下载链路结束后关闭页面提示",
149
+ "tool": "present_page_activity",
150
+ "args": {
151
+ "mode": "stop"
152
+ }
153
+ },
154
+ {
155
+ "nodeId": "s10",
156
+ "name": "switch_to_existing_host_tab",
157
+ "goal": "任务结束后若存在匹配的既有标签页则切回去",
158
+ "when": "exists(payload.returnHostPatterns)",
159
+ "tool": "switch_tab",
160
+ "args": {
161
+ "urlIncludesAny": "{{data.returnHostPatterns}}",
162
+ "optional": true
163
+ }
164
+ }
165
+ ]
166
+ }