n8n-nodes-kingsoft-airscript 2.0.7 → 2.0.8

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.
@@ -7,136 +7,165 @@ const API_CONFIG = {
7
7
  ENDPOINTS: {
8
8
  SYNC_TASK: '/api/v3/ide/file/:file_id/script/:script_id/sync_task',
9
9
  ASYNC_TASK: '/api/v3/ide/file/:file_id/script/:script_id/task',
10
- GET_TASK: '/api/v3/script/task'
11
- }
10
+ GET_TASK: '/api/v3/script/task',
11
+ },
12
12
  };
13
13
  const 创建并发闸门 = (上限) => {
14
- let running = 0;
15
- const queue = [];
16
- const acquire = async () => {
17
- if (running < 上限) {
18
- running++;
14
+ let 当前运行数 = 0;
15
+ const 等待队列 = [];
16
+ const 获取资源 = async () => {
17
+ if (当前运行数 < 上限) {
18
+ 当前运行数++;
19
19
  return;
20
20
  }
21
- await new Promise(resolve => queue.push(resolve));
22
- running++;
21
+ await new Promise((resolve) => 等待队列.push(resolve));
22
+ 当前运行数++;
23
23
  };
24
- const release = () => {
25
- running--;
26
- const next = queue.shift();
27
- if (next)
28
- next();
24
+ const 释放资源 = () => {
25
+ 当前运行数--;
26
+ const 下一个任务 = 等待队列.shift();
27
+ if (下一个任务)
28
+ 下一个任务();
29
29
  };
30
- return { acquire, release };
30
+ return { acquire: 获取资源, release: 释放资源 };
31
31
  };
32
32
  class KingsoftAirscript {
33
33
  constructor() {
34
34
  this.description = {
35
- displayName: 'Kingsoft AirScript 连接器',
35
+ displayName: 'Kingsoft AirScript 连接器 v3',
36
36
  name: 'kingsoftAirscript',
37
37
  icon: 'file:kingsoftAirscript.svg',
38
38
  group: ['transform'],
39
39
  version: 3,
40
40
  subtitle: '={{$parameter["operation"]}}',
41
- description: '连接并执行 Kingsoft AirScript 脚本,支持同步/异步执行、任务状态查询和智能批处理。',
41
+ description: '连接并执行 Kingsoft AirScript 脚本(V3:默认只执行一次 + 内部分批并发 + 汇总输出,杜绝重复写入)。',
42
42
  defaults: { name: 'Kingsoft AirScript' },
43
43
  inputs: ['main'],
44
44
  outputs: ['main'],
45
45
  credentials: [{ name: 'kingsoftAirscriptApi', required: true }],
46
46
  hints: [
47
47
  {
48
- message: "当处理大量输入项时,建议在节点设置中启用 <b>Execute Once</b> 选项以提高性能。",
48
+ message: 'V3 默认对 runScript 只执行一次。大数据请开启「自动分批写入」,让节点内部按 data 分批并发执行。',
49
49
  type: 'info',
50
50
  location: 'inputPane',
51
- whenToDisplay: 'always'
51
+ whenToDisplay: 'always',
52
52
  },
53
53
  {
54
- message: "全局最大并发数设置过高可能导致系统资源消耗过大,请根据服务器性能适当调整。",
54
+ message: '全局最大并发数设置过高可能导致系统资源消耗过大,请根据服务器性能适当调整。',
55
55
  type: 'warning',
56
56
  location: 'inputPane',
57
- whenToDisplay: 'always'
57
+ whenToDisplay: 'always',
58
58
  },
59
- {
60
- message: "处理大量数据时,建议启用自动分批写入功能,避免内存溢出。",
61
- type: 'info',
62
- location: 'inputPane',
63
- whenToDisplay: 'always'
64
- }
65
59
  ],
66
60
  properties: [
67
61
  {
68
- displayName: '📖 金山 AirScript 连接器 v3',
62
+ displayName: '📖 金山 AirScript 连接器 v3(推荐):默认只执行一次(避免重复写入),支持 argv 内数组字段分批并发执行,并可汇总输出。',
69
63
  name: 'notice',
70
64
  type: 'notice',
71
65
  default: '',
72
- description: '支持同步/异步执行脚本、自动分批处理和智能参数生成。',
66
+ description: 'V3:默认只执行一次(避免重复写入),支持 argv 内数组字段分批并发执行,并可汇总输出。',
73
67
  },
74
68
  {
75
- displayName: '数据输入模式',
76
- name: 'payloadMode',
69
+ displayName: '操作',
70
+ name: 'operation',
77
71
  type: 'options',
78
- default: 'argvFromUI',
72
+ noDataExpression: true,
79
73
  options: [
80
74
  {
81
- name: ' UI 配置 Argv(当前方式)',
82
- value: 'argvFromUI',
83
- description: 'Argv 完全由 UI 中的 JSON 模板决定,适合固定参数场景'
75
+ name: '同步执行脚本 (Sync)',
76
+ value: 'runScriptSync',
77
+ action: '同步执行脚本并等待结果',
78
+ description: '适用于需要立即获取执行结果的场景,执行时间较短时推荐'
84
79
  },
85
80
  {
86
- name: '从输入项直接作为 Argv',
87
- value: 'argvFromItem',
88
- description: '每个输入项的 $JSON 直接作为 argv,适合上游已整理好结构的场景'
81
+ name: '异步执行脚本 (Async)',
82
+ value: 'runScriptAsync',
83
+ action: '异步执行脚本并返回 Task id',
84
+ description: '适用于长时间运行的脚本,避免 n8n 工作流超时'
89
85
  },
90
86
  {
91
- name: '追加输入项到 Argv 数组字段',
92
- value: 'appendItemsToArrayField',
93
- description: '将所有输入项聚合成数组写入 argv,适合批量处理场景(只执行一次)'
87
+ name: '获取任务状态 (Get Status)',
88
+ value: 'getTaskStatus',
89
+ action: '根据 Task id 查询任务状态',
90
+ description: '用于查询异步执行任务的状态和结果'
94
91
  },
95
92
  ],
96
- description: '选择如何将 n8n 输入数据传递给脚本。',
97
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'] } },
98
- },
99
- {
100
- displayName: '目标数组字段路径',
101
- name: 'targetArrayField',
102
- type: 'string',
103
- default: 'data',
104
- hint: '例如:data 或 写表行列表',
105
- description: '当选择「追加输入项到 argv 数组字段」时,指定 argv 中的目标数组字段。例如设置为 "data",则所有输入项会被聚合到 argv.data 数组中。',
106
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], payloadMode: ['appendItemsToArrayField'] } },
107
- },
108
- {
109
- displayName: '项目数据字段路径',
110
- name: 'itemDataPath',
111
- type: 'string',
112
- default: '',
113
- hint: '留空表示使用整个 $json',
114
- description: '当选择「从输入项直接作为 argv」或「追加输入项到 argv 数组字段」时,指定从输入项中提取数据的字段路径。例如设置为 "payload",则会使用 $JSON.payload 作为数据来源。',
115
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], payloadMode: ['argvFromItem', 'appendItemsToArrayField'] } },
93
+ default: 'runScriptSync',
116
94
  },
117
95
  {
118
- displayName: '操作',
119
- name: 'operation',
96
+ displayName: '执行模式(V3)',
97
+ name: 'runModeV3',
120
98
  type: 'options',
121
99
  noDataExpression: true,
100
+ default: 'uiOnce',
101
+ displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'] } },
122
102
  options: [
123
- { name: '同步执行脚本 (Sync)', value: 'runScriptSync', action: '同步执行一个脚本并立即等待结果' },
124
- { name: '异步执行脚本 (Async)', value: 'runScriptAsync', action: '异步执行一个脚本并立即返回任务ID' },
125
- { name: '获取任务状态 (Get Status)', value: 'getTaskStatus', action: '根据任务ID获取脚本的执行状态和结果' },
103
+ {
104
+ name: '执行一次:仅使用 UI Argv(推荐,最安全)',
105
+ value: 'uiOnce',
106
+ description: '无论输入多少 items,本节点仅执行一次。适合 UI Argv 本身已包含 data 数组的批量写入。\n✅ 适用场景:金山文档中已配置好数据结构的场景,避免重复执行',
107
+ },
108
+ {
109
+ name: '执行一次:聚合输入 Items 到数组字段(默认 Data,可改)',
110
+ value: 'aggregateOnce',
111
+ description: '把所有输入 items 聚合成数组,写入到 Argv 的指定字段(默认 data),然后仅执行一次。\n✅ 适用场景:将 n8n 工作流中的多个数据项合并后批量处理',
112
+ },
113
+ ],
114
+ },
115
+ {
116
+ displayName: '聚合配置(Aggregate)',
117
+ name: 'aggregate',
118
+ type: 'collection',
119
+ placeholder: '添加聚合配置',
120
+ default: {},
121
+ displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], runModeV3: ['aggregateOnce'] } },
122
+ options: [
123
+ {
124
+ displayName: '目标数组字段路径(相对 Argv)',
125
+ name: 'targetArrayField',
126
+ type: 'string',
127
+ default: 'data',
128
+ description: '例:data 或 写表行列表 或 data.rows(支持点路径)\n⚠️ 这个字段名需要与 AirScript 脚本中期望接收数据的字段名一致',
129
+ },
130
+ {
131
+ displayName: '从输入项提取数据路径(相对 $Json)',
132
+ name: 'itemDataPath',
133
+ type: 'string',
134
+ default: '',
135
+ hint: '留空表示使用整个 $json',
136
+ description: '聚合时,每个输入 item 从哪个路径取值作为数组元素。\n💡 示例:data.rows 表示从每个输入项的 data.rows 字段提取数据',
137
+ },
138
+ {
139
+ displayName: '注入策略',
140
+ name: 'injectPolicy',
141
+ type: 'options',
142
+ default: 'overwrite',
143
+ options: [
144
+ { name: '强制覆盖目标字段(推荐,最确定)', value: 'overwrite', description: '无论目标字段是否存在,都强制覆盖' },
145
+ { name: '若目标字段不存在才注入', value: 'ifMissing', description: '仅在目标字段不存在时注入,避免意外覆盖' },
146
+ { name: '追加到目标字段(数组 Concat)', value: 'append', description: '将数据追加到现有数组末尾' },
147
+ ],
148
+ description: '决定聚合数组如何写入 Argv。\n🔒 推荐使用"强制覆盖",行为最明确',
149
+ },
126
150
  ],
127
- default: 'runScriptSync',
128
151
  },
129
152
  {
130
153
  displayName: 'ID 输入模式',
131
154
  name: 'idInputMode',
132
155
  type: 'options',
133
156
  noDataExpression: true,
134
- displayOptions: {
135
- show: { operation: ['runScriptSync', 'runScriptAsync'] },
136
- },
157
+ displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'] } },
137
158
  options: [
138
- { name: '通过 Webhook 链接输入', value: 'url', description: '粘贴完整的链接,最简单快捷' },
139
- { name: '手动输入 ID', value: 'manual', description: '分别填写 File ID 和 Script ID' },
159
+ {
160
+ name: '通过 Webhook 链接输入',
161
+ value: 'url',
162
+ description: '粘贴完整链接(推荐)\n✅ 从金山文档脚本页面直接复制链接,自动解析 ID,避免手动输入错误'
163
+ },
164
+ {
165
+ name: '手动输入 ID',
166
+ value: 'manual',
167
+ description: '分别填写 File ID 与 Script ID\n⚠️ 需要确保 ID 准确无误,建议优先使用链接方式'
168
+ },
140
169
  ],
141
170
  default: 'url',
142
171
  },
@@ -146,15 +175,10 @@ class KingsoftAirscript {
146
175
  type: 'string',
147
176
  default: '',
148
177
  required: true,
149
- hint: '格式示例:https://www.kdocs.cn/api/v3/ide/file/xxx/script/yyy/sync_task',
150
- displayOptions: {
151
- show: {
152
- operation: ['runScriptSync', 'runScriptAsync'],
153
- idInputMode: ['url'],
154
- },
155
- },
156
- placeholder: '在此粘贴从金山文档复制的完整链接',
157
- description: '推荐!最简单的方式。',
178
+ hint: '示例:https://www.kdocs.cn/api/v3/ide/file/xxx/script/yyy/sync_task',
179
+ displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], idInputMode: ['url'] } },
180
+ placeholder: '粘贴从金山文档复制的完整链接',
181
+ description: '将从链接中自动解析 File ID 与 Script ID。\n💡 获取方式:在金山文档中打开脚本,点击"复制链接"按钮',
158
182
  },
159
183
  {
160
184
  displayName: '文件 ID (File ID)',
@@ -162,14 +186,8 @@ class KingsoftAirscript {
162
186
  type: 'string',
163
187
  default: '',
164
188
  required: true,
165
- hint: ' Webhook 链接中提取的长字符串',
166
- displayOptions: {
167
- show: {
168
- operation: ['runScriptSync', 'runScriptAsync'],
169
- idInputMode: ['manual'],
170
- },
171
- },
172
- description: '脚本所在文档的 ID。',
189
+ displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], idInputMode: ['manual'] } },
190
+ description: '脚本所在文件的 ID。\n🔍 可在金山文档的 URL 中找到 file_id 参数',
173
191
  },
174
192
  {
175
193
  displayName: '脚本 ID (Script ID)',
@@ -177,161 +195,79 @@ class KingsoftAirscript {
177
195
  type: 'string',
178
196
  default: '',
179
197
  required: true,
180
- hint: ' Webhook 链接中提取的长字符串',
181
- displayOptions: {
182
- show: {
183
- operation: ['runScriptSync', 'runScriptAsync'],
184
- idInputMode: ['manual'],
185
- },
186
- },
187
- description: '要执行的脚本的 ID。',
198
+ displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], idInputMode: ['manual'] } },
199
+ description: '脚本 ID。\n🔍 可在金山文档的 URL 中找到 script_id 参数',
188
200
  },
189
201
  {
190
- displayName: '可选上下文参数',
202
+ displayName: '可选上下文参数(Context)',
191
203
  name: 'contextParameters',
192
204
  type: 'collection',
193
205
  placeholder: '添加可选参数',
194
206
  default: {},
195
- description: '设置可选的 Context 参数,会附加到请求中',
196
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'] } },
197
- options: [
198
- { displayName: '表名 (Sheet Name)', name: 'sheetName', type: 'string', default: '', description: '对应 Context.sheet_name' },
199
- { displayName: '范围 (Range)', name: 'range', type: 'string', default: '', description: '对应 Context.range, 例如 "$B$156"' },
200
- { displayName: '链接来源 (Link From)', name: 'linkFrom', type: 'string', default: '', description: '对应 Context.link_from' },
201
- { displayName: '视图名称 (DB Active View)', name: 'dbActiveView', type: 'string', default: '', description: '对应 Context.db_active_view' },
202
- { displayName: '选区 (DB Selection)', name: 'dbSelection', type: 'string', default: '', description: '对应 Context.db_selection' },
203
- ],
204
- },
205
- {
206
- displayName: '参数模板',
207
- name: 'argvTemplate',
208
- type: 'options',
209
- noDataExpression: true,
210
207
  displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'] } },
211
208
  options: [
212
- { name: '处理附件字段', value: 'attachment', description: '录入包含图片等附件的数据' },
213
- { name: '处理级联字段', value: 'cascade', description: '录入包含级联字段的数据' },
214
- { name: '基本数据录入', value: 'basicInput', description: '向指定数据表中录入新数据' },
215
- { name: '批量迁移数据', value: 'migrate', description: '将数据从一个表迁移到另一个表' },
216
- { name: '设置公式字段', value: 'formula', description: '向表中设置公式字段的表达式' },
217
- { name: '设置关联属性', value: 'setLinkProperties', description: '配置关联字段的属性' },
218
- { name: '设置级联选项', value: 'setCascadeOptions', description: '配置级联字段的选项结构' },
219
- { name: '使用 N8N 表达式', value: 'n8nExpression', description: '在 N8N 工作流中动态处理输入数据' },
220
- { name: '使用复合唯一键', value: 'compositeKey', description: '使用多个字段作为复合唯一键进行更新' },
221
- { name: '智能创建表格', value: 'createSheet', description: '根据数据结构智能创建表格和字段' },
222
- { name: '自定义 (手动输入)', value: 'custom', description: '手动输入 JSON 格式参数' },
209
+ {
210
+ displayName: '表名 (Sheet Name)',
211
+ name: 'sheetName',
212
+ type: 'string',
213
+ default: '',
214
+ description: '对应 Context.sheet_name\n💡 指定脚本操作的目标表格名称'
215
+ },
216
+ {
217
+ displayName: '范围 (Range)',
218
+ name: 'range',
219
+ type: 'string',
220
+ default: '',
221
+ description: '对应 Context.range\n💡 指定操作范围,如 A1:B10'
222
+ },
223
+ {
224
+ displayName: '链接来源 (Link From)',
225
+ name: 'linkFrom',
226
+ type: 'string',
227
+ default: '',
228
+ description: '对应 Context.link_from\n💡 用于跨表链接操作时指定来源'
229
+ },
230
+ {
231
+ displayName: '视图名称 (DB Active View)',
232
+ name: 'dbActiveView',
233
+ type: 'string',
234
+ default: '',
235
+ description: '对应 Context.db_active_view\n💡 指定数据库视图名称'
236
+ },
237
+ {
238
+ displayName: '选区 (DB Selection)',
239
+ name: 'dbSelection',
240
+ type: 'string',
241
+ default: '',
242
+ description: '对应 Context.db_selection\n💡 指定数据库选区范围'
243
+ },
223
244
  ],
224
- default: 'custom',
225
- description: '选择预设模板后,会显示对应的 Argv JSON 输入框(可编辑)',
226
- },
227
- {
228
- displayName: '脚本参数 (Argv)',
229
- name: 'argv_custom',
230
- type: 'json',
231
- default: '{\n "message": "Hello from n8n!"\n}',
232
- required: true,
233
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['custom'] } },
234
- description: '自定义 JSON 参数。 数据输入模式说明: - argvFromUI:完全使用此模板作为 argv - argvFromItem:此模板被忽略,使用输入项的 $JSON - appendItemsToArrayField:此模板作为基础,输入项会被聚合成数组添加到指定字段',
235
- },
236
- {
237
- displayName: '脚本参数 (Argv)',
238
- name: 'argv_basicInput',
239
- type: 'json',
240
- default: `{\n "sheet_name": "客户表",\n "覆盖模式": false,\n "data": [\n {\n "客户名称": "张三",\n "联系方式": "13800138000",\n "邮箱": "zhangsan@example.com"\n }\n ]\n}`,
241
- required: true,
242
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['basicInput'] } },
243
- description: '基本数据录入模板参数(可编辑)',
244
245
  },
245
246
  {
246
- displayName: '脚本参数 (Argv)',
247
- name: 'argv_attachment',
248
- type: 'json',
249
- default: `{\n "sheet_name": "产品表",\n "覆盖模式": true,\n "唯一键": "产品ID",\n "data": [\n {\n "产品ID": "P001",\n "产品名称": "XXX沙发",\n "图片": "https://example.com/image1.jpg",\n "描述": "舒适的三人沙发"\n }\n ]\n}`,
250
- required: true,
251
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['attachment'] } },
252
- description: '处理附件字段模板参数(可编辑)',
253
- },
254
- {
255
- displayName: '脚本参数 (Argv)',
256
- name: 'argv_cascade',
257
- type: 'json',
258
- default: `{\n "sheet_name": "订单表",\n "覆盖模式": true,\n "唯一键": "订单ID",\n "data": [\n {\n "订单ID": "O001",\n "客户": "张三",\n "地区": "广东省",\n "城市": "深圳市",\n "产品": "XXX沙发"\n }\n ]\n}`,
259
- required: true,
260
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['cascade'] } },
261
- description: '处理级联字段模板参数(可编辑)',
262
- },
263
- {
264
- displayName: '脚本参数 (Argv)',
265
- name: 'argv_migrate',
266
- type: 'json',
267
- default: `{\n "源表名称": "旧客户表",\n "目标表名称": "新客户表",\n "映射关系": {\n "旧ID": "客户ID",\n "姓名": "客户名称",\n "电话": "联系方式"\n },\n "过滤条件": "状态 = '活跃'"\n}`,
268
- required: true,
269
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['migrate'] } },
270
- description: '批量迁移数据模板参数(可编辑)',
271
- },
272
- {
273
- displayName: '脚本参数 (Argv)',
274
- name: 'argv_formula',
275
- type: 'json',
276
- default: `{\n "sheet_name": "销售表",\n "字段配置": [\n {\n "字段名称": "销售额",\n "字段类型": "公式",\n "表达式": "单价 * 数量"\n }\n ]\n}`,
277
- required: true,
278
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['formula'] } },
279
- description: '设置公式字段模板参数(可编辑)',
280
- },
281
- {
282
- displayName: '脚本参数 (Argv)',
283
- name: 'argv_setLinkProperties',
284
- type: 'json',
285
- default: `{\n "sheet_name": "订单表",\n "关联字段": "客户ID",\n "目标表": "客户表",\n "显示字段": ["客户名称", "联系方式"]\n}`,
286
- required: true,
287
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['setLinkProperties'] } },
288
- description: '设置关联属性模板参数(可编辑)',
289
- },
290
- {
291
- displayName: '脚本参数 (Argv)',
292
- name: 'argv_setCascadeOptions',
293
- type: 'json',
294
- default: `{\n "sheet_name": "地区表",\n "级联字段": "地区",\n "选项结构": {\n "广东省": ["深圳市", "广州市", "东莞市"],\n "浙江省": ["杭州市", "宁波市", "温州市"]\n }\n}`,
295
- required: true,
296
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['setCascadeOptions'] } },
297
- description: '设置级联选项模板参数(可编辑)',
298
- },
299
- {
300
- displayName: '脚本参数 (Argv)',
301
- name: 'argv_n8nExpression',
302
- type: 'json',
303
- default: `{\n "sheet_name": "动态表",\n "覆盖模式": true,\n "唯一键": "ID",\n "data": [\n {\n "ID": "={{ $json.id }}",\n "名称": "={{ $json.name }}",\n "值": "={{ $json.value }}"\n }\n ]\n}`,
304
- required: true,
305
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['n8nExpression'] } },
306
- description: '使用 N8N 表达式模板参数(可编辑)',
307
- },
308
- {
309
- displayName: '脚本参数 (Argv)',
310
- name: 'argv_compositeKey',
311
- type: 'json',
312
- default: `{\n "sheet_name": "库存表",\n "覆盖模式": true,\n "唯一键": ["产品ID", "仓库ID"],\n "data": [\n {\n "产品ID": "P001",\n "仓库ID": "W001",\n "库存数量": 100,\n "最后更新": "2024-01-01"\n }\n ]\n}`,
247
+ displayName: '任务 ID (Task ID)',
248
+ name: 'taskId',
249
+ type: 'string',
250
+ default: '',
313
251
  required: true,
314
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['compositeKey'] } },
315
- description: '使用复合唯一键模板参数(可编辑)',
252
+ displayOptions: { show: { operation: ['getTaskStatus'] } },
253
+ description: '异步执行返回的 task_id。',
316
254
  },
317
255
  {
318
- displayName: '脚本参数 (Argv)',
319
- name: 'argv_createSheet',
256
+ displayName: '脚本参数(Argv JSON)',
257
+ name: 'argv',
320
258
  type: 'json',
321
- default: `{\n "sheet_name": "新表格",\n "字段定义": [\n {\n "名称": "ID",\n "类型": "文本",\n "唯一": true\n },\n {\n "名称": "名称",\n "类型": "文本"\n },\n {\n "名称": "数值",\n "类型": "数字"\n },\n {\n "名称": "日期",\n "类型": "日期"\n }\n ],\n "数据": [\n {\n "ID": "1",\n "名称": "测试1",\n "数值": 100,\n "日期": "2024-01-01"\n }\n ]\n}`,
259
+ default: '{\n "sheet_name": "示例表",\n "data": []\n}',
322
260
  required: true,
323
- displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'], argvTemplate: ['createSheet'] } },
324
- description: '智能创建表格模板参数(可编辑)',
261
+ displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'] } },
262
+ description: '最终会作为 Context.argv 发送给 AirScript。V3 不提供多模板,避免困惑。',
325
263
  },
326
264
  {
327
- displayName: '任务 ID (Task ID)',
328
- name: 'taskId',
329
- type: 'string',
265
+ displayName: 'Argv 示例(可复制)常用示例(复制到「脚本参数 Argv JSON」即可): 1)执行一次(UI Argv 自带 data 数组) { "sheet_name": "示例表", "data": [] } 2)执行一次(用表达式聚合输入 items) { "sheet_name": "示例表", "data": "={{ $input.all().map(i => i.JSON) }}" } 3)聚合模式(推荐) - 执行模式:aggregateOnce - 目标数组字段路径:data - itemDataPath 留空(使用整个 $JSON)',
266
+ name: 'argvExamples',
267
+ type: 'notice',
330
268
  default: '',
331
- required: true,
332
- hint: '从异步执行结果中获取,格式为长字符串',
333
- displayOptions: { show: { operation: ['getTaskStatus'] } },
334
- description: '从异步执行脚本操作中获取到的任务 ID',
269
+ displayOptions: { show: { operation: ['runScriptSync', 'runScriptAsync'] } },
270
+ description: '常用示例(复制到「脚本参数 Argv JSON」即可): 1)执行一次(UI Argv 自带 data 数组) { "sheet_name": "示例表", "data": [] } 2)执行一次(用表达式聚合输入 items) { "sheet_name": "示例表", "data": "={{ $input.all().map(i => i.JSON) }}" } 3)聚合模式(推荐) - 执行模式:aggregateOnce - 目标数组字段路径:data - itemDataPath 留空(使用整个 $JSON)'
335
271
  },
336
272
  {
337
273
  displayName: '高级选项',
@@ -339,14 +275,14 @@ class KingsoftAirscript {
339
275
  type: 'collection',
340
276
  placeholder: '添加高级选项',
341
277
  default: {},
342
- description: '配置异步等待、输出格式和性能优化。',
278
+ description: '超时/重试/分批/调试/并发等配置。\n💡 根据具体需求选择相应的配置项',
343
279
  options: [
344
280
  {
345
- displayName: '并行执行',
281
+ displayName: '并行执行(主要用于 getTaskStatus)',
346
282
  name: 'a_parallelExecution',
347
283
  type: 'boolean',
348
284
  default: false,
349
- description: 'Whether enabled, 节点将并行处理所有输入项',
285
+ description: 'Whether 开启后将并行处理多个输入项(对 runScript 默认只跑一次影响不大)',
350
286
  },
351
287
  {
352
288
  displayName: '查询任务超时(秒)',
@@ -366,49 +302,48 @@ class KingsoftAirscript {
366
302
  description: '异步等待完成模式下,最多等待的总时长(秒)。',
367
303
  },
368
304
  {
369
- displayName: '等待执行完成',
305
+ displayName: '等待执行完成(异步轮询)',
370
306
  name: 'c_waitForCompletion',
371
307
  type: 'boolean',
372
308
  default: false,
373
- description: 'Whether enabled, 异步执行时节点将轮询直到任务完成',
374
309
  displayOptions: { show: { '/operation': ['runScriptAsync'] } },
310
+ description: 'Whether开启后:异步执行会轮询直到 finished/failed 或超时。',
375
311
  },
376
312
  {
377
313
  displayName: '调试模式(输出 _调试 字段)',
378
314
  name: 'w_debug',
379
315
  type: 'boolean',
380
316
  default: false,
381
- description: 'Whether enabled, 输出中会包含 `_调试` 字段,包含延迟和重试详情',
317
+ description: 'Whether 开启后输出包含 `_调试` 字段,包含执行摘要、重试与分批信息',
382
318
  },
383
319
  {
384
320
  displayName: '分批输出模式',
385
321
  name: 'v_chunkOutputMode',
386
322
  type: 'options',
387
- default: 'split',
323
+ default: 'merge',
388
324
  options: [
389
- { name: '逐批输出(推荐)', value: 'split' },
390
- { name: '合并为一个 Item(批次输出列表)', value: 'merge' },
325
+ { name: '逐批输出(Split)', value: 'split' },
326
+ { name: '汇总为一个 Item(merge,推荐)', value: 'merge' },
391
327
  ],
392
328
  displayOptions: { show: { '/options/r_autoChunkEnabled': [true] } },
393
- description: '自动分批时的输出策略。',
329
+ description: '分批执行时的输出策略。',
394
330
  },
395
331
  {
396
332
  displayName: '分批字段路径(相对 Argv)',
397
333
  name: 's_chunkFieldPath',
398
334
  type: 'string',
399
- default: '写表行列表',
400
- hint: '指定包含大量数据的数组字段',
401
- description: '例:写表行列表 或 data.写表行列表(从 argv 根开始)',
335
+ default: 'data',
336
+ description: '默认 data。也可填 写表行列表 / rows / data.rows 等(从 argv 根开始)。',
402
337
  displayOptions: { show: { '/options/r_autoChunkEnabled': [true] } },
403
338
  },
404
339
  {
405
- displayName: '分批最大并发数(同一 Item 内)',
340
+ displayName: '分批最大并发数(同一执行内)',
406
341
  name: 'u_chunkMaxConcurrency',
407
342
  type: 'number',
408
343
  default: 3,
409
344
  typeOptions: { minValue: 1, maxValue: 20 },
410
345
  displayOptions: { show: { '/options/r_autoChunkEnabled': [true] } },
411
- description: '自动分批时,同一输入 item 内批次执行的最大并发数。',
346
+ description: '分批时,同时执行的批次数上限。',
412
347
  },
413
348
  {
414
349
  displayName: '轮询抖动(毫秒)',
@@ -416,7 +351,7 @@ class KingsoftAirscript {
416
351
  type: 'number',
417
352
  default: 300,
418
353
  typeOptions: { minValue: 0 },
419
- description: '在轮询间隔上叠加随机抖动,避免同一秒集中刷接口。',
354
+ description: '轮询间隔叠加随机抖动,避免集中刷接口。',
420
355
  },
421
356
  {
422
357
  displayName: '轮询间隔 (秒)',
@@ -428,23 +363,22 @@ class KingsoftAirscript {
428
363
  description: '异步等待完成模式下的轮询间隔(秒)。',
429
364
  },
430
365
  {
431
- displayName: '每批条数',
366
+ displayName: '每批条数(Chunk Size)',
432
367
  name: 't_chunkSize',
433
368
  type: 'number',
434
369
  default: 200,
435
- hint: '处理大量数据时建议减小此值',
436
370
  typeOptions: { minValue: 1 },
437
371
  displayOptions: { show: { '/options/r_autoChunkEnabled': [true] } },
438
- description: '自动分批时,每批数组元素数量。',
372
+ description: '分批时每批数组元素数量(可自定义)。',
439
373
  },
440
374
  {
441
- displayName: '批处理数量',
375
+ displayName: '批处理数量(并行 Item 批大小)',
442
376
  name: 'e_batchSize',
443
377
  type: 'number',
444
378
  default: 10,
445
379
  typeOptions: { minValue: 1 },
446
380
  displayOptions: { show: { '/options/a_parallelExecution': [true] } },
447
- description: '并行执行开启时,每批并发处理的输入 item 数量。',
381
+ description: '并行执行开启时,每批并行处理的输入 item 数量。',
448
382
  },
449
383
  {
450
384
  displayName: '请求超时(秒)',
@@ -455,27 +389,26 @@ class KingsoftAirscript {
455
389
  description: '调用 sync_task/task 的 HTTP 超时(秒)。',
456
390
  },
457
391
  {
458
- displayName: '全局最大并发数',
392
+ displayName: '全局最大并发数(HTTP 请求总闸门)',
459
393
  name: 'x_globalMaxConcurrency',
460
394
  type: 'number',
461
395
  default: 5,
462
- hint: '建议根据服务器性能设置,默认 5',
463
396
  typeOptions: { minValue: 1, maxValue: 50 },
464
- description: '全局最大并发 HTTP 请求数,防止并发乘法爆炸(输入并发 × 批次并发)。',
397
+ description: '防止并发乘法爆炸(例如:分批并发 × 并行执行)。',
465
398
  },
466
399
  {
467
400
  displayName: '失败判定:Finished 但 Error 不为空算失败',
468
401
  name: 'n_failOnScriptError',
469
402
  type: 'boolean',
470
403
  default: true,
471
- description: 'Whether enabled, 将 `status=finished``error` 不为空的情况视为失败',
404
+ description: 'Whether 开启后:status=finished 但 error/error_details.msg 不为空也视为失败',
472
405
  },
473
406
  {
474
407
  displayName: '输出格式',
475
408
  name: 'f_outputFormat',
476
409
  type: 'options',
477
410
  default: 'fullResponse',
478
- displayOptions: { show: { '/operation': ['runScriptSync', 'runScriptAsync'] } },
411
+ displayOptions: { show: { '/operation': ['runScriptSync', 'runScriptAsync', 'getTaskStatus'] } },
479
412
  options: [
480
413
  { name: '完整响应', value: 'fullResponse' },
481
414
  { name: '仅结果', value: 'resultOnly' },
@@ -484,12 +417,43 @@ class KingsoftAirscript {
484
417
  ],
485
418
  },
486
419
  {
487
- displayName: '智能解析 Result',
420
+ displayName: '危险:每个输入项执行一次(慎用)',
421
+ name: 'y_dangerEachItemEnabled',
422
+ type: 'boolean',
423
+ default: false,
424
+ description: 'Whether to execute AirScript once per input item (n items = n calls, easily causing duplicate writes)\n⚠️ 强烈警告:启用此选项可能导致数据重复写入,请确保业务逻辑需要多次执行!',
425
+ },
426
+ {
427
+ displayName: '危险模式:合并 UI Argv(不推荐)',
428
+ name: 'ab_eachItemMergeUiArgv',
429
+ type: 'boolean',
430
+ default: false,
431
+ displayOptions: { show: { '/options/y_dangerEachItemEnabled': [true] } },
432
+ description: 'Whether默认关闭:避免 UI 中 data 数组被每项重复执行导致成倍写入。\n⚠️ 启用后,每个输入项都会包含 UI Argv 中的数据,可能导致数据重复',
433
+ },
434
+ {
435
+ displayName: '危险模式:每项 Argv 来源路径(相对 $Json)',
436
+ name: 'aa_eachItemDataPath',
437
+ type: 'string',
438
+ default: '',
439
+ displayOptions: { show: { '/options/y_dangerEachItemEnabled': [true] } },
440
+ description: '留空表示使用整个 $JSON 作为 argv。\n💡 示例:data.rows 表示从每个输入项的 data.rows 字段提取数据',
441
+ },
442
+ {
443
+ displayName: '危险模式:确认多次执行',
444
+ name: 'z_confirmDangerEachItem',
445
+ type: 'boolean',
446
+ default: false,
447
+ displayOptions: { show: { '/options/y_dangerEachItemEnabled': [true] } },
448
+ description: 'Whether必须勾选才允许执行(用于防止误触)。\n🔒 安全机制:防止误触危险模式,确保用户明确知道风险',
449
+ },
450
+ {
451
+ displayName: '智能解析 Result(若 Result 是 JSON 字符串)',
488
452
  name: 'g_parseResultString',
489
453
  type: 'boolean',
490
454
  default: false,
491
- description: 'Whether enabled, 如果 `result` JSON 字符串,将自动解析',
492
- displayOptions: { show: { '/operation': ['runScriptSync', 'runScriptAsync'] } },
455
+ displayOptions: { show: { '/operation': ['runScriptSync', 'runScriptAsync', 'getTaskStatus'] } },
456
+ description: 'Whether 开启后:若 data.result JSON 字符串,将自动 JSON.parse',
493
457
  },
494
458
  {
495
459
  displayName: '重试初始等待(毫秒)',
@@ -512,8 +476,7 @@ class KingsoftAirscript {
512
476
  name: 'r_autoChunkEnabled',
513
477
  type: 'boolean',
514
478
  default: false,
515
- hint: '处理大量数据时建议启用',
516
- description: 'Whether enabled, 会按配置的字段路径分割 argv 中的数组,并分块运行脚本',
479
+ description: 'Whether 开启后:按字段路径拆分 argv 内数组,分批并发执行脚本',
517
480
  },
518
481
  {
519
482
  displayName: '最大重试次数',
@@ -528,7 +491,7 @@ class KingsoftAirscript {
528
491
  name: 'm_honorRetryAfter',
529
492
  type: 'boolean',
530
493
  default: true,
531
- description: 'Whether enabled, 当收到带有 Retry-After 头的 HTTP 429 响应时,会按指示等待后重试',
494
+ description: 'Whether收到 429 且响应头带 Retry-After 时,按指示等待后重试。',
532
495
  },
533
496
  ],
534
497
  },
@@ -538,608 +501,679 @@ class KingsoftAirscript {
538
501
  }
539
502
  async execute() {
540
503
  const 输入数据项列表 = this.getInputData();
504
+ const 节点 = this.getNode();
541
505
  const 凭证 = (await this.getCredentials('kingsoftAirscriptApi'));
542
- const 读取高级选项 = (索引) => {
543
- const raw = this.getNodeParameter('options', 索引, {});
544
- const 取数 = (k, d) => {
545
- const v = raw[k];
546
- const n = typeof v === 'number' ? v : Number(v);
547
- return Number.isFinite(n) ? n : d;
548
- };
549
- const 取布尔 = (k, d) => {
550
- const v = raw[k];
551
- return typeof v === 'boolean' ? v : d;
506
+ const 选项模块 = (() => {
507
+ const 读取高级选项 = (索引) => {
508
+ const raw = this.getNodeParameter('options', 索引, {});
509
+ const 取数 = (k, d) => {
510
+ const v = raw[k];
511
+ const n = typeof v === 'number' ? v : Number(v);
512
+ return Number.isFinite(n) ? n : d;
513
+ };
514
+ const 取布尔 = (k, d) => {
515
+ const v = raw[k];
516
+ return typeof v === 'boolean' ? v : d;
517
+ };
518
+ const 取字符串 = (k, d) => {
519
+ const v = raw[k];
520
+ return typeof v === 'string' ? v : d;
521
+ };
522
+ return {
523
+ a_parallelExecution: 取布尔('a_parallelExecution', false),
524
+ e_batchSize: 取数('e_batchSize', 10),
525
+ b_timeoutSeconds: 取数('b_timeoutSeconds', 300),
526
+ c_waitForCompletion: 取布尔('c_waitForCompletion', false),
527
+ d_pollIntervalSeconds: 取数('d_pollIntervalSeconds', 5),
528
+ o_pollJitterMs: 取数('o_pollJitterMs', 300),
529
+ f_outputFormat: 取字符串('f_outputFormat', 'fullResponse'),
530
+ g_parseResultString: 取布尔('g_parseResultString', false),
531
+ h_requestTimeoutSeconds: 取数('h_requestTimeoutSeconds', 120),
532
+ i_statusTimeoutSeconds: 取数('i_statusTimeoutSeconds', 30),
533
+ j_maxRetries: 取数('j_maxRetries', 2),
534
+ k_retryInitialDelayMs: 取数('k_retryInitialDelayMs', 800),
535
+ l_retryBackoffFactor: 取数('l_retryBackoffFactor', 2),
536
+ m_honorRetryAfter: 取布尔('m_honorRetryAfter', true),
537
+ n_failOnScriptError: 取布尔('n_failOnScriptError', true),
538
+ r_autoChunkEnabled: 取布尔('r_autoChunkEnabled', false),
539
+ s_chunkFieldPath: 取字符串('s_chunkFieldPath', 'data'),
540
+ t_chunkSize: 取数('t_chunkSize', 200),
541
+ u_chunkMaxConcurrency: 取数('u_chunkMaxConcurrency', 3),
542
+ v_chunkOutputMode: 取字符串('v_chunkOutputMode', 'merge'),
543
+ w_debug: 取布尔('w_debug', false),
544
+ x_globalMaxConcurrency: 取数('x_globalMaxConcurrency', 5),
545
+ y_dangerEachItemEnabled: 取布尔('y_dangerEachItemEnabled', false),
546
+ z_confirmDangerEachItem: 取布尔('z_confirmDangerEachItem', false),
547
+ aa_eachItemDataPath: 取字符串('aa_eachItemDataPath', ''),
548
+ ab_eachItemMergeUiArgv: 取布尔('ab_eachItemMergeUiArgv', false),
549
+ };
552
550
  };
553
- const 取字符串 = (k, d) => {
554
- const v = raw[k];
555
- return typeof v === 'string' ? v : d;
551
+ const 创建重试配置 = (opt) => ({
552
+ 最大重试次数: opt.j_maxRetries,
553
+ 初始等待毫秒: opt.k_retryInitialDelayMs,
554
+ 退避倍数: opt.l_retryBackoffFactor,
555
+ 尊重RetryAfter: opt.m_honorRetryAfter,
556
+ });
557
+ return { 读取高级选项, 创建重试配置 };
558
+ })();
559
+ const 工具模块 = (() => {
560
+ const 等待毫秒 = async (ms) => {
561
+ if (ms <= 0)
562
+ return;
563
+ await (0, n8n_workflow_1.sleep)(ms);
556
564
  };
557
- return {
558
- a_parallelExecution: 取布尔('a_parallelExecution', false),
559
- b_timeoutSeconds: 取数('b_timeoutSeconds', 300),
560
- c_waitForCompletion: 取布尔('c_waitForCompletion', false),
561
- d_pollIntervalSeconds: 取数('d_pollIntervalSeconds', 5),
562
- e_batchSize: 取数('e_batchSize', 10),
563
- f_outputFormat: 取字符串('f_outputFormat', 'fullResponse'),
564
- g_parseResultString: 取布尔('g_parseResultString', false),
565
- x_globalMaxConcurrency: 取数('x_globalMaxConcurrency', 5),
566
- h_requestTimeoutSeconds: 取数('h_requestTimeoutSeconds', 120),
567
- i_statusTimeoutSeconds: 取数('i_statusTimeoutSeconds', 30),
568
- j_maxRetries: 取数('j_maxRetries', 2),
569
- k_retryInitialDelayMs: 取数('k_retryInitialDelayMs', 800),
570
- l_retryBackoffFactor: 取数('l_retryBackoffFactor', 2),
571
- m_honorRetryAfter: 取布尔('m_honorRetryAfter', true),
572
- n_failOnScriptError: 取布尔('n_failOnScriptError', true),
573
- o_pollJitterMs: 取数('o_pollJitterMs', 300),
574
- r_autoChunkEnabled: 取布尔('r_autoChunkEnabled', false),
575
- s_chunkFieldPath: 取字符串('s_chunkFieldPath', '写表行列表'),
576
- t_chunkSize: 取数('t_chunkSize', 200),
577
- u_chunkMaxConcurrency: 取数('u_chunkMaxConcurrency', 3),
578
- v_chunkOutputMode: 取字符串('v_chunkOutputMode', 'split'),
579
- w_debug: 取布尔('w_debug', false),
565
+ const 脱敏ID = (id) => {
566
+ const s = (id !== null && id !== void 0 ? id : '').toString();
567
+ if (s.length <= 10)
568
+ return s;
569
+ return `${s.slice(0, 4)}***${s.slice(-4)}`;
580
570
  };
581
- };
582
- const opt0 = 读取高级选项(0);
583
- const 全局并发闸门 = 创建并发闸门(Math.max(1, opt0.x_globalMaxConcurrency));
584
- const 等待毫秒 = async (ms) => {
585
- if (ms <= 0)
586
- return;
587
- const 开始时间 = Date.now();
588
- while (Date.now() - 开始时间 < ms) {
589
- await new Promise((resolve) => {
590
- Promise.resolve().then(() => resolve());
591
- });
592
- }
593
- };
594
- const 脱敏ID = (id) => {
595
- const s = (id !== null && id !== void 0 ? id : '').toString();
596
- if (s.length <= 10)
597
- return s;
598
- return `${s.slice(0, 4)}***${s.slice(-4)}`;
599
- };
600
- const 解析RetryAfter毫秒 = (v) => {
601
- if (v === null || v === undefined)
602
- return null;
603
- const s = String(v).trim();
604
- if (/^\d+$/.test(s))
605
- return Number(s) * 1000;
606
- const t = Date.parse(s);
607
- if (Number.isFinite(t)) {
608
- const diff = t - Date.now();
609
- return diff > 0 ? diff : 0;
610
- }
611
- return null;
612
- };
613
- const 提取Http错误 = (e) => { var _a; return (_a = e) !== null && _a !== void 0 ? _a : {}; };
614
- const 是IDataObject = (v) => {
615
- return typeof v === 'object' && v !== null && !Array.isArray(v);
616
- };
617
- const getStatusCode = (err) => {
618
- var _a, _b, _c, _d;
619
- return (_c = (_b = (_a = err.response) === null || _a === void 0 ? void 0 : _a.status) !== null && _b !== void 0 ? _b : err.statusCode) !== null && _c !== void 0 ? _c : (_d = err.response) === null || _d === void 0 ? void 0 : _d.statusCode;
620
- };
621
- const getBody = (err) => {
622
- var _a, _b, _c, _d;
623
- return ((_c = (_b = (_a = err.response) === null || _a === void 0 ? void 0 : _a.data) !== null && _b !== void 0 ? _b : err.body) !== null && _c !== void 0 ? _c : (_d = err.response) === null || _d === void 0 ? void 0 : _d.body);
624
- };
625
- const getHeaders = (err) => {
626
- var _a, _b, _c, _d;
627
- return (_c = (_b = (_a = err.response) === null || _a === void 0 ? void 0 : _a.headers) !== null && _b !== void 0 ? _b : err.headers) !== null && _c !== void 0 ? _c : (_d = err.response) === null || _d === void 0 ? void 0 : _d.headers;
628
- };
629
- const 提取错误消息 = (err) => {
630
- const data = getBody(err);
631
- if (是IDataObject(data)) {
632
- const errorDetails = data.error_details;
633
- if (是IDataObject(errorDetails)) {
634
- const msg = errorDetails.msg;
635
- if (typeof msg === 'string' && msg.trim())
636
- return msg.trim();
571
+ const 解析RetryAfter毫秒 = (v) => {
572
+ if (v === null || v === undefined)
573
+ return null;
574
+ const s = String(v).trim();
575
+ if (/^\d+$/.test(s))
576
+ return Number(s) * 1000;
577
+ const t = Date.parse(s);
578
+ if (Number.isFinite(t)) {
579
+ const diff = t - Date.now();
580
+ return diff > 0 ? diff : 0;
637
581
  }
638
- const error = data.error;
639
- if (typeof error === 'string' && error.trim())
640
- return error.trim();
641
- }
642
- if (typeof err.message === 'string' && err.message.trim())
643
- return err.message.trim();
644
- return '未知错误';
645
- };
646
- const 判断是否可重试 = (e) => {
647
- const err = 提取Http错误(e);
648
- const 错误码 = err.code;
649
- const 状态码 = getStatusCode(err);
650
- return (['ETIMEDOUT', 'ECONNRESET', 'EAI_AGAIN', 'ECONNREFUSED', 'ECONNABORTED'].includes(String(错误码 !== null && 错误码 !== void 0 ? 错误码 : '')) ||
651
- [408, 425, 429, 500, 502, 503, 504].includes(Number(状态码 !== null && 状态码 !== void 0 ? 状态码 : 0)));
652
- };
653
- const 带重试 = async (执行函数, 配置, 调试) => {
654
- var _a, _b;
655
- let 尝试 = 0;
656
- while (true) {
657
- try {
658
- const res = await 执行函数();
659
- if (调试)
660
- 调试.重试次数 = 尝试;
661
- return res;
662
- }
663
- catch (e) {
664
- const err = 提取Http错误(e);
665
- const 状态码 = getStatusCode(err);
666
- if (!判断是否可重试(e) || 尝试 >= 配置.最大重试次数) {
667
- if (调试)
668
- 调试.重试次数 = 尝试;
669
- throw e;
670
- }
671
- let 等待 = Math.round(配置.初始等待毫秒 * Math.pow(配置.退避倍数, 尝试) + Math.random() * 200);
672
- if (配置.尊重RetryAfter && 状态码 === 429) {
673
- const headers = (_a = getHeaders(err)) !== null && _a !== void 0 ? _a : {};
674
- const ra = (_b = headers['retry-after']) !== null && _b !== void 0 ? _b : headers['Retry-After'];
675
- const raMs = 解析RetryAfter毫秒(ra);
676
- if (raMs !== null)
677
- 等待 = Math.max(等待, raMs);
582
+ return null;
583
+ };
584
+ const 是IDataObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
585
+ const 并发执行 = async (任务函数列表, 并发数) => {
586
+ const 结果 = new Array(任务函数列表.length);
587
+ let 下一个索引 = 0;
588
+ const 工人 = async () => {
589
+ while (true) {
590
+ const i = 下一个索引++;
591
+ if (i >= 任务函数列表.length)
592
+ break;
593
+ 结果[i] = await 任务函数列表[i]();
678
594
  }
679
- if (调试)
680
- 调试.最后等待毫秒 = 等待;
681
- await 等待毫秒(等待);
682
- 尝试++;
683
- }
684
- }
685
- };
686
- const 并发执行 = async (任务函数列表, 并发数) => {
687
- const 结果 = new Array(任务函数列表.length);
688
- let 下一个索引 = 0;
689
- const 工人 = async () => {
690
- while (true) {
691
- const i = 下一个索引++;
692
- if (i >= 任务函数列表.length)
693
- break;
694
- 结果[i] = await 任务函数列表[i]();
695
- }
595
+ };
596
+ const 工人数量 = Math.min(Math.max(1, 并发数), 任务函数列表.length);
597
+ await Promise.all(Array.from({ length: 工人数量 }, () => 工人()));
598
+ return 结果;
696
599
  };
697
- const 工人数量 = Math.min(Math.max(1, 并发数), 任务函数列表.length);
698
- await Promise.all(Array.from({ length: 工人数量 }, () => 工人()));
699
- return 结果;
700
- };
701
- const 取路径值 = (obj, path) => {
702
- if (!obj || !path)
703
- return undefined;
704
- const keys = String(path).split('.').filter(Boolean);
705
- let cur = obj;
706
- for (const k of keys) {
707
- if (cur === null || cur === undefined || typeof cur !== 'object')
600
+ const 取路径值 = (obj, path) => {
601
+ if (!obj || !path)
708
602
  return undefined;
709
- cur = cur[k];
710
- }
711
- return cur;
712
- };
713
- const 以不可变方式写入路径 = (obj, path, value) => {
714
- const keys = String(path).split('.').filter(Boolean);
715
- if (keys.length === 0)
716
- return obj;
717
- const 递归 = (cur, idx) => {
718
- const k = keys[idx];
719
- const isLast = idx === keys.length - 1;
720
- if (Array.isArray(cur) && !isLast) {
721
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `路径中间节点不能是数组:${keys.slice(0, idx + 1).join('.')} 是数组,无法在其下创建子字段`);
603
+ const keys = String(path).split('.').filter(Boolean);
604
+ let cur = obj;
605
+ for (const k of keys) {
606
+ if (cur === null || cur === undefined || typeof cur !== 'object')
607
+ return undefined;
608
+ cur = cur[k];
722
609
  }
723
- const base = (cur && typeof cur === 'object' && !Array.isArray(cur)) ? cur : {};
724
- const clone = { ...base };
725
- if (isLast)
726
- clone[k] = value;
727
- else
728
- clone[k] = 递归(base[k], idx + 1);
729
- return clone;
610
+ return cur;
730
611
  };
731
- return 递归(obj, 0);
732
- };
733
- const 提取脚本错误消息 = (响应) => {
734
- var _a;
735
- const error = String((_a = 响应.error) !== null && _a !== void 0 ? _a : '').trim();
736
- if (error)
737
- return error;
738
- const details = 响应.error_details;
739
- if (details && typeof details === 'object' && !Array.isArray(details)) {
740
- const msg = details.msg;
741
- if (typeof msg === 'string' && msg.trim())
742
- return msg.trim();
743
- }
744
- return '';
745
- };
746
- const 校验脚本是否失败 = (响应, itemIndex, 开启) => {
747
- if (!开启)
748
- return;
749
- const status = typeof 响应.status === 'string' ? 响应.status : '';
750
- const errorMsg = 提取脚本错误消息(响应);
751
- if (status === 'finished' && errorMsg) {
752
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `脚本执行失败:${errorMsg}`, { itemIndex });
753
- }
754
- };
755
- const 提取TaskId = (响应) => {
756
- var _a, _b;
757
- const fromData = (_a = 响应.data) === null || _a === void 0 ? void 0 : _a.task_id;
758
- const fromTop = 响应.task_id;
759
- return String((_b = fromData !== null && fromData !== void 0 ? fromData : fromTop) !== null && _b !== void 0 ? _b : '').trim();
760
- };
761
- const 解析执行信息 = (itemIndex, inputData) => {
762
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
763
- const id输入模式 = this.getNodeParameter('idInputMode', itemIndex, 'url');
764
- let 文件ID = '';
765
- let 脚本ID = '';
766
- if (id输入模式 === 'url') {
767
- const webhook链接 = this.getNodeParameter('webhookUrl', itemIndex, '');
768
- try {
769
- const url对象 = new URL(webhook链接);
770
- const 路径分段 = url对象.pathname.split('/');
771
- const 文件ID索引 = 路径分段.indexOf('file');
772
- const 脚本ID索引 = 路径分段.indexOf('script');
773
- if (文件ID索引 !== -1 && 脚本ID索引 !== -1 && 路径分段.length > Math.max(文件ID索引, 脚本ID索引) + 1) {
774
- 文件ID = 路径分段[文件ID索引 + 1];
775
- 脚本ID = 路径分段[脚本ID索引 + 1];
612
+ const 以不可变方式写入路径 = (obj, path, value) => {
613
+ const keys = String(path).split('.').filter(Boolean);
614
+ if (keys.length === 0)
615
+ return obj;
616
+ const 递归 = (cur, idx) => {
617
+ const k = keys[idx];
618
+ const isLast = idx === keys.length - 1;
619
+ if (Array.isArray(cur) && !isLast) {
620
+ throw new n8n_workflow_1.NodeOperationError(节点, `路径中间节点不能是数组:${keys.slice(0, idx + 1).join('.')} 是数组,无法在其下创建子字段`);
776
621
  }
777
- else {
778
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Webhook 链接格式不正确,无法解析出 File ID 和 Script ID。', { itemIndex });
622
+ const base = (cur && typeof cur === 'object' && !Array.isArray(cur)) ? cur : {};
623
+ const clone = { ...base };
624
+ if (isLast)
625
+ clone[k] = value;
626
+ else
627
+ clone[k] = 递归(base[k], idx + 1);
628
+ return clone;
629
+ };
630
+ return 递归(obj, 0);
631
+ };
632
+ const 第一层智能反序列化 = (argv参数) => {
633
+ for (const key in argv参数) {
634
+ if (!Object.prototype.hasOwnProperty.call(argv参数, key))
635
+ continue;
636
+ const value = argv参数[key];
637
+ if (typeof value !== 'string')
638
+ continue;
639
+ const trimmed = value.trim();
640
+ if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
641
+ (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
642
+ try {
643
+ argv参数[key] = JSON.parse(trimmed);
644
+ }
645
+ catch {
646
+ }
779
647
  }
780
648
  }
781
- catch {
782
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Webhook 链接格式不正确,无法解析出 File ID 和 Script ID。', { itemIndex });
649
+ };
650
+ return {
651
+ 等待毫秒,
652
+ 脱敏ID,
653
+ 解析RetryAfter毫秒,
654
+ 是IDataObject,
655
+ 并发执行,
656
+ 取路径值,
657
+ 以不可变方式写入路径,
658
+ 第一层智能反序列化,
659
+ };
660
+ })();
661
+ const 错误模块 = (() => {
662
+ const 提取Http错误 = (e) => { var _a; return (_a = e) !== null && _a !== void 0 ? _a : {}; };
663
+ const 获取状态码 = (err) => {
664
+ var _a, _b, _c, _d;
665
+ return (_c = (_b = (_a = err.response) === null || _a === void 0 ? void 0 : _a.status) !== null && _b !== void 0 ? _b : err.statusCode) !== null && _c !== void 0 ? _c : (_d = err.response) === null || _d === void 0 ? void 0 : _d.statusCode;
666
+ };
667
+ const 获取响应体 = (err) => {
668
+ var _a, _b, _c, _d;
669
+ return ((_c = (_b = (_a = err.response) === null || _a === void 0 ? void 0 : _a.data) !== null && _b !== void 0 ? _b : err.body) !== null && _c !== void 0 ? _c : (_d = err.response) === null || _d === void 0 ? void 0 : _d.body);
670
+ };
671
+ const 获取响应头 = (err) => {
672
+ var _a, _b, _c, _d;
673
+ return (_c = (_b = (_a = err.response) === null || _a === void 0 ? void 0 : _a.headers) !== null && _b !== void 0 ? _b : err.headers) !== null && _c !== void 0 ? _c : (_d = err.response) === null || _d === void 0 ? void 0 : _d.headers;
674
+ };
675
+ const 提取错误消息 = (err) => {
676
+ const 响应体数据 = 获取响应体(err);
677
+ if (工具模块.是IDataObject(响应体数据)) {
678
+ const 错误详情 = 响应体数据.error_details;
679
+ if (工具模块.是IDataObject(错误详情)) {
680
+ const 错误消息 = 错误详情.msg;
681
+ if (typeof 错误消息 === 'string' && 错误消息.trim())
682
+ return 错误消息.trim();
683
+ }
684
+ const 错误信息 = 响应体数据.error;
685
+ if (typeof 错误信息 === 'string' && 错误信息.trim())
686
+ return 错误信息.trim();
783
687
  }
784
- }
785
- else {
786
- 文件ID = this.getNodeParameter('fileId', itemIndex, '');
787
- 脚本ID = this.getNodeParameter('scriptId', itemIndex, '');
788
- }
789
- if (!文件ID || !脚本ID) {
790
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), '未能获取到有效的 File ID 和 Script ID。', { itemIndex });
791
- }
792
- const payloadMode = this.getNodeParameter('payloadMode', itemIndex, 'argvFromUI');
793
- let argv参数 = {};
794
- const 可选上下文参数 = this.getNodeParameter('contextParameters', itemIndex, {});
795
- let 最终_sheet_name = '';
796
- let 最终_range = '';
797
- let 最终_link_from = '';
798
- let 最终_db_active_view = '';
799
- let 最终_db_selection = '';
800
- const 模板到字段名 = {
801
- custom: 'argv_custom',
802
- basicInput: 'argv_basicInput',
803
- attachment: 'argv_attachment',
804
- cascade: 'argv_cascade',
805
- migrate: 'argv_migrate',
806
- formula: 'argv_formula',
807
- setLinkProperties: 'argv_setLinkProperties',
808
- setCascadeOptions: 'argv_setCascadeOptions',
809
- n8nExpression: 'argv_n8nExpression',
810
- compositeKey: 'argv_compositeKey',
811
- createSheet: 'argv_createSheet',
688
+ if (typeof err.message === 'string' && err.message.trim())
689
+ return err.message.trim();
690
+ return '未知错误';
691
+ };
692
+ const 判断是否可重试 = (e) => {
693
+ const 错误 = 提取Http错误(e);
694
+ const 错误码 = 错误.code;
695
+ const 状态码 = 获取状态码(错误);
696
+ return (['ETIMEDOUT', 'ECONNRESET', 'EAI_AGAIN', 'ECONNREFUSED', 'ECONNABORTED'].includes(String(错误码 !== null && 错误码 !== void 0 ? 错误码 : '')) ||
697
+ [408, 425, 429, 500, 502, 503, 504].includes(Number(状态码 !== null && 状态码 !== void 0 ? 状态码 : 0)));
812
698
  };
813
- if (payloadMode === 'argvFromUI') {
814
- const argvTemplate = this.getNodeParameter('argvTemplate', itemIndex, 'custom');
815
- const argv字段名 = (_a = 模板到字段名[argvTemplate]) !== null && _a !== void 0 ? _a : 'argv_custom';
816
- const argv原始 = this.getNodeParameter(argv字段名, itemIndex, {});
817
- if (typeof argv原始 === 'string') {
699
+ return { 提取Http错误, 获取状态码, 获取响应体, 获取响应头, 提取错误消息, 判断是否可重试 };
700
+ })();
701
+ const 重试模块 = (() => {
702
+ const 带重试 = async (执行函数, 配置, 调试) => {
703
+ var _a, _b;
704
+ let 尝试次数 = 0;
705
+ while (true) {
818
706
  try {
819
- argv参数 = JSON.parse(argv原始);
707
+ const 执行结果 = await 执行函数();
708
+ if (调试)
709
+ 调试.重试次数 = 尝试次数;
710
+ return 执行结果;
820
711
  }
821
- catch {
822
- argv参数 = { value: argv原始 };
712
+ catch (错误) {
713
+ const 实际错误 = 错误模块.提取Http错误(错误);
714
+ const 状态码 = 错误模块.获取状态码(实际错误);
715
+ if (!错误模块.判断是否可重试(错误) || 尝试次数 >= 配置.最大重试次数) {
716
+ if (调试)
717
+ 调试.重试次数 = 尝试次数;
718
+ throw 错误;
719
+ }
720
+ let 等待毫秒数 = Math.round(配置.初始等待毫秒 * Math.pow(配置.退避倍数, 尝试次数) + Math.random() * 200);
721
+ if (配置.尊重RetryAfter && 状态码 === 429) {
722
+ const 响应头 = (_a = 错误模块.获取响应头(实际错误)) !== null && _a !== void 0 ? _a : {};
723
+ const 重试后 = (_b = 响应头['retry-after']) !== null && _b !== void 0 ? _b : 响应头['Retry-After'];
724
+ const 重试后毫秒 = 工具模块.解析RetryAfter毫秒(重试后);
725
+ if (重试后毫秒 !== null)
726
+ 等待毫秒数 = Math.max(等待毫秒数, 重试后毫秒);
727
+ }
728
+ if (调试)
729
+ 调试.最后等待毫秒 = 等待毫秒数;
730
+ await 工具模块.等待毫秒(等待毫秒数);
731
+ 尝试次数++;
823
732
  }
824
733
  }
825
- else if (argv原始 && typeof argv原始 === 'object' && !Array.isArray(argv原始)) {
826
- argv参数 = argv原始;
734
+ };
735
+ return { 带重试 };
736
+ })();
737
+ const 脚本模块 = (() => {
738
+ const 提取脚本错误消息 = (响应) => {
739
+ var _a;
740
+ const error = String((_a = 响应.error) !== null && _a !== void 0 ? _a : '').trim();
741
+ if (error)
742
+ return error;
743
+ const details = 响应.error_details;
744
+ if (details && typeof details === 'object' && !Array.isArray(details)) {
745
+ const msg = details.msg;
746
+ if (typeof msg === 'string' && msg.trim())
747
+ return msg.trim();
748
+ }
749
+ return '';
750
+ };
751
+ const 校验脚本是否失败 = (响应, itemIndex, 开启) => {
752
+ if (!开启)
753
+ return;
754
+ const status = typeof 响应.status === 'string' ? 响应.status : '';
755
+ const errorMsg = 提取脚本错误消息(响应);
756
+ if (status === 'finished' && errorMsg) {
757
+ throw new n8n_workflow_1.NodeOperationError(节点, `脚本执行失败:${errorMsg}`, { itemIndex });
827
758
  }
828
- }
829
- else if (payloadMode === 'argvFromItem') {
830
- const itemDataPath = this.getNodeParameter('itemDataPath', itemIndex, '');
831
- const item = (inputData === null || inputData === void 0 ? void 0 : inputData[itemIndex]) || 输入数据项列表[itemIndex];
832
- if (item === null || item === void 0 ? void 0 : item.json) {
833
- if (itemDataPath) {
834
- const 数据值 = 取路径值(item.json, itemDataPath);
835
- if (数据值 !== undefined) {
836
- if (typeof 数据值 === 'object' && 数据值 !== null && !Array.isArray(数据值))
837
- argv参数 = { ...数据值 };
838
- else
839
- argv参数 = { value: 数据值 };
759
+ };
760
+ const 提取TaskId = (响应) => {
761
+ var _a, _b;
762
+ const fromData = (_a = 响应.data) === null || _a === void 0 ? void 0 : _a.task_id;
763
+ const fromTop = 响应.task_id;
764
+ return String((_b = fromData !== null && fromData !== void 0 ? fromData : fromTop) !== null && _b !== void 0 ? _b : '').trim();
765
+ };
766
+ return { 校验脚本是否失败, 提取TaskId };
767
+ })();
768
+ const 参数模块 = (() => {
769
+ const 解析文件脚本ID = (itemIndex) => {
770
+ const id输入模式 = this.getNodeParameter('idInputMode', itemIndex, 'url');
771
+ let 文件ID = '';
772
+ let 脚本ID = '';
773
+ if (id输入模式 === 'url') {
774
+ const webhook链接 = this.getNodeParameter('webhookUrl', itemIndex, '');
775
+ try {
776
+ const url对象 = new URL(webhook链接);
777
+ const 路径分段 = url对象.pathname.split('/');
778
+ const 文件ID索引 = 路径分段.indexOf('file');
779
+ const 脚本ID索引 = 路径分段.indexOf('script');
780
+ if (文件ID索引 !== -1 &&
781
+ 脚本ID索引 !== -1 &&
782
+ 路径分段.length > Math.max(文件ID索引, 脚本ID索引) + 1) {
783
+ 文件ID = 路径分段[文件ID索引 + 1];
784
+ 脚本ID = 路径分段[脚本ID索引 + 1];
785
+ }
786
+ else {
787
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Webhook 链接格式不正确,无法解析出 File ID 和 Script ID。', { itemIndex });
840
788
  }
841
789
  }
842
- else {
843
- argv参数 = { ...item.json };
790
+ catch {
791
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Webhook 链接格式不正确,无法解析出 File ID 和 Script ID。', { itemIndex });
844
792
  }
845
793
  }
846
- }
847
- else if (payloadMode === 'appendItemsToArrayField') {
848
- const targetArrayField = this.getNodeParameter('targetArrayField', itemIndex, 'data');
849
- const itemDataPath = this.getNodeParameter('itemDataPath', itemIndex, '');
850
- const 输入数据 = inputData || 输入数据项列表;
851
- const argvTemplate = this.getNodeParameter('argvTemplate', itemIndex, 'custom');
852
- const argv字段名 = (_b = 模板到字段名[argvTemplate]) !== null && _b !== void 0 ? _b : 'argv_custom';
853
- const argv原始 = this.getNodeParameter(argv字段名, itemIndex, {});
854
- if (typeof argv原始 === 'string') {
794
+ else {
795
+ 文件ID = this.getNodeParameter('fileId', itemIndex, '');
796
+ 脚本ID = this.getNodeParameter('scriptId', itemIndex, '');
797
+ }
798
+ if (!文件ID || !脚本ID) {
799
+ throw new n8n_workflow_1.NodeOperationError(节点, '未能获取到有效的 File ID 和 Script ID。', { itemIndex });
800
+ }
801
+ return { 文件ID, 脚本ID };
802
+ };
803
+ const 读取UIArgv = (itemIndex) => {
804
+ const raw = this.getNodeParameter('argv', itemIndex, {});
805
+ if (typeof raw === 'string') {
855
806
  try {
856
- argv参数 = JSON.parse(argv原始);
807
+ return JSON.parse(raw);
857
808
  }
858
809
  catch {
859
- argv参数 = { value: argv原始 };
810
+ return { value: raw };
860
811
  }
861
812
  }
862
- else if (argv原始 && typeof argv原始 === 'object' && !Array.isArray(argv原始)) {
863
- argv参数 = argv原始;
864
- }
865
- const 目标数组 = [];
866
- for (const item of 输入数据) {
867
- if (!(item === null || item === void 0 ? void 0 : item.json))
868
- continue;
869
- if (itemDataPath) {
870
- const 数据值 = 取路径值(item.json, itemDataPath);
871
- if (数据值 !== undefined)
872
- 目标数组.push(数据值);
813
+ if (raw && typeof raw === 'object' && !Array.isArray(raw))
814
+ return raw;
815
+ return {};
816
+ };
817
+ const 读取执行模式V3 = (itemIndex) => {
818
+ return this.getNodeParameter('runModeV3', itemIndex, 'uiOnce');
819
+ };
820
+ const 取输入项数据 = (item, 路径) => {
821
+ if (!(item === null || item === void 0 ? void 0 : item.json))
822
+ return undefined;
823
+ if (!路径)
824
+ return item.json;
825
+ return 工具模块.取路径值(item.json, 路径);
826
+ };
827
+ const 合并Context = (itemIndex, argv参数, 调试字段) => {
828
+ const 可选上下文参数 = this.getNodeParameter('contextParameters', itemIndex, {});
829
+ const 规范化文本 = (v) => {
830
+ if (v === null || v === undefined)
831
+ return '';
832
+ if (typeof v === 'string')
833
+ return v.trim();
834
+ return String(v).trim();
835
+ };
836
+ const 从Argv取 = (snakeKey, camelKey) => {
837
+ var _a;
838
+ const v = (_a = argv参数[snakeKey]) !== null && _a !== void 0 ? _a : argv参数[camelKey];
839
+ return 规范化文本(v);
840
+ };
841
+ const 合并一个字段 = (参数) => {
842
+ const argv值 = 从Argv取(参数.snakeKey, 参数.camelKey);
843
+ const context值 = 规范化文本(可选上下文参数[参数.contextKey]);
844
+ if (argv值 && context值 && argv值 !== context值) {
845
+ if (调试字段) {
846
+ if (!调试字段.Context合并冲突)
847
+ 调试字段.Context合并冲突 = [];
848
+ 调试字段.Context合并冲突.push({
849
+ 字段: 参数.字段中文名,
850
+ argv: argv值,
851
+ contextParameters: context值,
852
+ 最终采用: 'argv优先',
853
+ });
854
+ }
873
855
  }
874
- else {
875
- 目标数组.push(item.json);
856
+ const 最终值 = argv值 || context值;
857
+ if (最终值) {
858
+ argv参数[参数.snakeKey] = 最终值;
876
859
  }
860
+ return 最终值;
861
+ };
862
+ const 最终_sheet_name = 合并一个字段({ 字段中文名: '表名', snakeKey: 'sheet_name', camelKey: 'sheetName', contextKey: 'sheetName' });
863
+ const 最终_range = 合并一个字段({ 字段中文名: '范围', snakeKey: 'range', camelKey: 'range', contextKey: 'range' });
864
+ const 最终_link_from = 合并一个字段({ 字段中文名: '链接来源', snakeKey: 'link_from', camelKey: 'linkFrom', contextKey: 'linkFrom' });
865
+ const 最终_db_active_view = 合并一个字段({ 字段中文名: '视图名称', snakeKey: 'db_active_view', camelKey: 'dbActiveView', contextKey: 'dbActiveView' });
866
+ const 最终_db_selection = 合并一个字段({ 字段中文名: '选区', snakeKey: 'db_selection', camelKey: 'dbSelection', contextKey: 'dbSelection' });
867
+ const 上下文 = { argv: argv参数 };
868
+ if (最终_sheet_name)
869
+ 上下文.sheet_name = 最终_sheet_name;
870
+ if (最终_range)
871
+ 上下文.range = 最终_range;
872
+ if (最终_link_from)
873
+ 上下文.link_from = 最终_link_from;
874
+ if (最终_db_active_view)
875
+ 上下文.db_active_view = 最终_db_active_view;
876
+ if (最终_db_selection)
877
+ 上下文.db_selection = 最终_db_selection;
878
+ return 上下文;
879
+ };
880
+ const 聚合输入项到数组 = (itemIndex, 目标路径, 提取路径, 策略, uiArgv) => {
881
+ const 聚合数组 = [];
882
+ for (const item of 输入数据项列表) {
883
+ const v = 取输入项数据(item, 提取路径);
884
+ if (v !== undefined)
885
+ 聚合数组.push(v);
877
886
  }
878
- if (targetArrayField.includes('.'))
879
- argv参数 = 以不可变方式写入路径(argv参数, targetArrayField, 目标数组);
880
- else
881
- argv参数[targetArrayField] = 目标数组;
882
- }
883
- for (const key in argv参数) {
884
- if (!Object.prototype.hasOwnProperty.call(argv参数, key))
885
- continue;
886
- const value = argv参数[key];
887
- if (typeof value !== 'string')
888
- continue;
889
- const trimmed = value.trim();
890
- if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
891
- try {
892
- argv参数[key] = JSON.parse(trimmed);
893
- }
894
- catch (e) {
895
- void e;
887
+ const 当前值 = 工具模块.取路径值(uiArgv, 目标路径);
888
+ if (策略 === 'ifMissing') {
889
+ if (当前值 !== undefined)
890
+ return uiArgv;
891
+ return 工具模块.以不可变方式写入路径(uiArgv, 目标路径, 聚合数组);
892
+ }
893
+ if (策略 === 'append') {
894
+ if (Array.isArray(当前值)) {
895
+ return 工具模块.以不可变方式写入路径(uiArgv, 目标路径, [...当前值, ...聚合数组]);
896
896
  }
897
+ return 工具模块.以不可变方式写入路径(uiArgv, 目标路径, 聚合数组);
897
898
  }
898
- }
899
- const 从Argv提取 = (snakeKey, camelKey) => {
900
- if (argv参数[snakeKey] !== undefined)
901
- return argv参数[snakeKey];
902
- if (argv参数[camelKey] !== undefined)
903
- return argv参数[camelKey];
904
- return undefined;
899
+ return 工具模块.以不可变方式写入路径(uiArgv, 目标路径, 聚合数组);
905
900
  };
906
- 最终_sheet_name = String((_d = (_c = 从Argv提取('sheet_name', 'sheetName')) !== null && _c !== void 0 ? _c : 可选上下文参数.sheetName) !== null && _d !== void 0 ? _d : '').trim();
907
- 最终_range = String((_f = (_e = 从Argv提取('range', 'range')) !== null && _e !== void 0 ? _e : 可选上下文参数.range) !== null && _f !== void 0 ? _f : '').trim();
908
- 最终_link_from = String((_h = (_g = 从Argv提取('link_from', 'linkFrom')) !== null && _g !== void 0 ? _g : 可选上下文参数.linkFrom) !== null && _h !== void 0 ? _h : '').trim();
909
- 最终_db_active_view = String((_k = (_j = 从Argv提取('db_active_view', 'dbActiveView')) !== null && _j !== void 0 ? _j : 可选上下文参数.dbActiveView) !== null && _k !== void 0 ? _k : '').trim();
910
- 最终_db_selection = String((_m = (_l = 从Argv提取('db_selection', 'dbSelection')) !== null && _l !== void 0 ? _l : 可选上下文参数.dbSelection) !== null && _m !== void 0 ? _m : '').trim();
911
- if (最终_sheet_name)
912
- argv参数.sheet_name = 最终_sheet_name;
913
- if (最终_range)
914
- argv参数.range = 最终_range;
915
- if (最终_link_from)
916
- argv参数.link_from = 最终_link_from;
917
- if (最终_db_active_view)
918
- argv参数.db_active_view = 最终_db_active_view;
919
- if (最终_db_selection)
920
- argv参数.db_selection = 最终_db_selection;
921
- const 上下文 = { argv: argv参数 };
922
- if (最终_sheet_name)
923
- 上下文.sheet_name = 最终_sheet_name;
924
- if (最终_range)
925
- 上下文.range = 最终_range;
926
- if (最终_link_from)
927
- 上下文.link_from = 最终_link_from;
928
- if (最终_db_active_view)
929
- 上下文.db_active_view = 最终_db_active_view;
930
- if (最终_db_selection)
931
- 上下文.db_selection = 最终_db_selection;
932
- return { 文件ID, 脚本ID, argv参数, 上下文 };
933
- };
934
- const 调用脚本接口 = async (参数) => {
935
- var _a, _b;
936
- const endpoint = 参数.类型 === 'sync' ? API_CONFIG.ENDPOINTS.SYNC_TASK : API_CONFIG.ENDPOINTS.ASYNC_TASK;
937
- const 请求URL = `${API_CONFIG.BASE_URL}${endpoint}`
938
- .replace(':file_id', 参数.文件ID)
939
- .replace(':script_id', 参数.脚本ID);
940
- const t0 = Date.now();
941
- const 调试统计 = {};
942
- const options = {
943
- method: 'POST',
944
- url: 请求URL,
945
- headers: {
946
- 'Content-Type': 'application/json',
947
- 'AirScript-Token': 凭证.apiToken,
948
- },
949
- body: { Context: 参数.上下文 },
950
- json: true,
951
- timeout: 参数.请求超时毫秒,
901
+ const 构建Argv_V3安全模式 = (itemIndex) => {
902
+ var _a, _b, _c;
903
+ const 执行模式 = 读取执行模式V3(itemIndex);
904
+ const uiArgv = 读取UIArgv(itemIndex);
905
+ 工具模块.第一层智能反序列化(uiArgv);
906
+ if (执行模式 === 'uiOnce') {
907
+ return uiArgv;
908
+ }
909
+ const 聚合配置 = this.getNodeParameter('aggregate', itemIndex, {});
910
+ const 目标数组字段路径 = String((_a = 聚合配置.targetArrayField) !== null && _a !== void 0 ? _a : 'data');
911
+ const 项目数据路径 = String((_b = 聚合配置.itemDataPath) !== null && _b !== void 0 ? _b : '');
912
+ const 注入 = ((_c = 聚合配置.injectPolicy) !== null && _c !== void 0 ? _c : 'overwrite');
913
+ return 聚合输入项到数组(itemIndex, 目标数组字段路径, 项目数据路径, 注入, uiArgv);
952
914
  };
953
- const res = (await 带重试(async () => {
954
- await 参数.并发闸门.acquire();
955
- try {
956
- return (await this.helpers.httpRequest(options));
915
+ const 构建Argv_危险每项模式 = (itemIndex, opt) => {
916
+ const 每项路径 = opt.aa_eachItemDataPath;
917
+ const item = 输入数据项列表[itemIndex];
918
+ const 每项原始 = 取输入项数据(item, 每项路径);
919
+ let 每项Argv = {};
920
+ if (每项原始 && typeof 每项原始 === 'object' && !Array.isArray(每项原始)) {
921
+ 每项Argv = { ...每项原始 };
957
922
  }
958
- finally {
959
- 参数.并发闸门.release();
923
+ else if (每项原始 !== undefined) {
924
+ 每项Argv = { value: 每项原始 };
960
925
  }
961
- }, 参数.重试, 调试统计));
962
- if (参数.调试) {
963
- 参数.调试['执行脚本'] = {
964
- url: `/api/v3/ide/file/${脱敏ID(参数.文件ID)}/script/${脱敏ID(参数.脚本ID)}/${参数.类型 === 'sync' ? 'sync_task' : 'task'}`,
965
- 耗时毫秒: Date.now() - t0,
966
- 重试次数: (_a = 调试统计.重试次数) !== null && _a !== void 0 ? _a : 0,
967
- 最后等待毫秒: (_b = 调试统计.最后等待毫秒) !== null && _b !== void 0 ? _b : null,
968
- };
969
- }
970
- return res;
971
- };
972
- const 获取任务状态 = async (参数) => {
973
- var _a, _b, _c;
974
- if (!参数.taskId || !参数.taskId.trim()) {
975
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'taskId 不能为空', { itemIndex: (_a = 参数.itemIndex) !== null && _a !== void 0 ? _a : 0 });
976
- }
977
- const 请求URL = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.GET_TASK}?task_id=${encodeURIComponent(参数.taskId)}`;
978
- const t0 = Date.now();
979
- const 调试统计 = {};
980
- const options = {
981
- method: 'GET',
982
- url: 请求URL,
983
- headers: { 'Content-Type': 'application/json', 'AirScript-Token': 凭证.apiToken },
984
- json: true,
985
- timeout: 参数.状态超时毫秒,
926
+ 工具模块.第一层智能反序列化(每项Argv);
927
+ if (!opt.ab_eachItemMergeUiArgv)
928
+ return 每项Argv;
929
+ const uiArgv = 读取UIArgv(0);
930
+ 工具模块.第一层智能反序列化(uiArgv);
931
+ const 数据字段 = opt.s_chunkFieldPath || 'data';
932
+ const uiData = 工具模块.取路径值(uiArgv, 数据字段);
933
+ if (输入数据项列表.length > 1 && Array.isArray(uiData) && uiData.length > 0) {
934
+ throw new n8n_workflow_1.NodeOperationError(节点, `危险模式下你开启了「合并 UI Argv」,且 UI Argv 的 ${数据字段} 是一个长度为 ${uiData.length} 的数组。\n` +
935
+ `这会导致重复写入(例如:10 个输入项 × data 10 条 => 100 条)。\n` +
936
+ `建议关闭「合并 UI Argv」,或关闭危险模式,或改用「执行一次」。`, { itemIndex: 0 });
937
+ }
938
+ return { ...uiArgv, ...每项Argv };
986
939
  };
987
- const res = (await 带重试(async () => {
988
- await 参数.并发闸门.acquire();
989
- try {
990
- return (await this.helpers.httpRequest(options));
940
+ return {
941
+ 解析文件脚本ID,
942
+ 读取执行模式V3,
943
+ 构建Argv_V3安全模式,
944
+ 构建Argv_危险每项模式,
945
+ 合并Context,
946
+ };
947
+ })();
948
+ const 网络模块 = (() => {
949
+ const 调用脚本接口 = async (参数) => {
950
+ var _a, _b;
951
+ const 接口端点 = 参数.类型 === 'sync' ? API_CONFIG.ENDPOINTS.SYNC_TASK : API_CONFIG.ENDPOINTS.ASYNC_TASK;
952
+ const 请求URL = `${API_CONFIG.BASE_URL}${接口端点}`
953
+ .replace(':file_id', 参数.文件ID)
954
+ .replace(':script_id', 参数.脚本ID);
955
+ const 开始时间 = Date.now();
956
+ const 调试统计 = {};
957
+ const 请求选项 = {
958
+ method: 'POST',
959
+ url: 请求URL,
960
+ headers: {
961
+ 'Content-Type': 'application/json',
962
+ 'AirScript-Token': 凭证.apiToken,
963
+ },
964
+ body: { Context: 参数.上下文 },
965
+ json: true,
966
+ timeout: 参数.请求超时毫秒,
967
+ };
968
+ const 响应结果 = (await 重试模块.带重试(async () => {
969
+ await 参数.并发闸门.acquire();
970
+ try {
971
+ return (await this.helpers.httpRequest(请求选项));
972
+ }
973
+ finally {
974
+ 参数.并发闸门.release();
975
+ }
976
+ }, 参数.重试, 调试统计));
977
+ if (参数.调试) {
978
+ 参数.调试['执行脚本'] = {
979
+ url: `/api/v3/ide/file/${工具模块.脱敏ID(参数.文件ID)}/script/${工具模块.脱敏ID(参数.脚本ID)}/${参数.类型 === 'sync' ? 'sync_task' : 'task'}`,
980
+ 耗时毫秒: Date.now() - 开始时间,
981
+ 重试次数: (_a = 调试统计.重试次数) !== null && _a !== void 0 ? _a : 0,
982
+ 最后等待毫秒: (_b = 调试统计.最后等待毫秒) !== null && _b !== void 0 ? _b : null,
983
+ };
991
984
  }
992
- finally {
993
- 参数.并发闸门.release();
985
+ return 响应结果;
986
+ };
987
+ const 获取任务状态 = async (参数) => {
988
+ var _a, _b, _c;
989
+ if (!参数.taskId || !参数.taskId.trim()) {
990
+ throw new n8n_workflow_1.NodeOperationError(节点, 'taskId 不能为空', { itemIndex: (_a = 参数.itemIndex) !== null && _a !== void 0 ? _a : 0 });
994
991
  }
995
- }, 参数.重试, 调试统计));
996
- if (参数.调试) {
997
- 参数.调试['获取任务状态'] = {
998
- task_id: 脱敏ID(参数.taskId),
999
- 耗时毫秒: Date.now() - t0,
1000
- 重试次数: (_b = 调试统计.重试次数) !== null && _b !== void 0 ? _b : 0,
1001
- 最后等待毫秒: (_c = 调试统计.最后等待毫秒) !== null && _c !== void 0 ? _c : null,
992
+ const 请求URL = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.GET_TASK}?task_id=${encodeURIComponent(参数.taskId)}`;
993
+ const 开始时间 = Date.now();
994
+ const 调试统计 = {};
995
+ const 请求选项 = {
996
+ method: 'GET',
997
+ url: 请求URL,
998
+ headers: { 'Content-Type': 'application/json', 'AirScript-Token': 凭证.apiToken },
999
+ json: true,
1000
+ timeout: 参数.状态超时毫秒,
1002
1001
  };
1003
- }
1004
- return res;
1005
- };
1006
- const 等待异步任务完成 = async (参数) => {
1007
- const 截止时间 = Date.now() + 参数.总等待超时毫秒;
1008
- let 首次 = true;
1009
- let 轮询次数 = 0;
1010
- while (Date.now() < 截止时间) {
1011
- if (!首次) {
1012
- const 抖动 = 参数.轮询抖动毫秒 > 0 ? Math.floor(Math.random() * 参数.轮询抖动毫秒) : 0;
1013
- await 等待毫秒(参数.轮询间隔毫秒 + 抖动);
1002
+ const 响应结果 = (await 重试模块.带重试(async () => {
1003
+ await 参数.并发闸门.acquire();
1004
+ try {
1005
+ return (await this.helpers.httpRequest(请求选项));
1006
+ }
1007
+ finally {
1008
+ 参数.并发闸门.release();
1009
+ }
1010
+ }, 参数.重试, 调试统计));
1011
+ if (参数.调试) {
1012
+ 参数.调试['获取任务状态'] = {
1013
+ task_id: 工具模块.脱敏ID(参数.taskId),
1014
+ 耗时毫秒: Date.now() - 开始时间,
1015
+ 重试次数: (_b = 调试统计.重试次数) !== null && _b !== void 0 ? _b : 0,
1016
+ 最后等待毫秒: (_c = 调试统计.最后等待毫秒) !== null && _c !== void 0 ? _c : null,
1017
+ };
1014
1018
  }
1015
- 首次 = false;
1016
- const 状态响应 = await 获取任务状态({
1017
- taskId: 参数.taskId,
1018
- 状态超时毫秒: 参数.状态超时毫秒,
1019
- 重试: 参数.重试,
1020
- 调试: 参数.调试,
1021
- 并发闸门: 参数.并发闸门,
1022
- });
1023
- const 状态 = typeof 状态响应.status === 'string' ? 状态响应.status.toLowerCase() : '';
1024
- const 终端状态 = ['finished', 'failed', 'canceled', 'cancelled', 'error'];
1025
- if (终端状态.includes(状态)) {
1026
- if (状态 === 'finished') {
1027
- 校验脚本是否失败(状态响应, 参数.itemIndex, 参数.finished但error算失败);
1019
+ return 响应结果;
1020
+ };
1021
+ const 等待异步任务完成 = async (参数) => {
1022
+ var _a, _b;
1023
+ const 截止时间 = Date.now() + 参数.总等待超时毫秒;
1024
+ let 首次 = true;
1025
+ let 轮询次数 = 0;
1026
+ while (Date.now() < 截止时间) {
1027
+ if (!首次) {
1028
+ const 抖动时间 = 参数.轮询抖动毫秒 > 0 ? Math.floor(Math.random() * 参数.轮询抖动毫秒) : 0;
1029
+ await 工具模块.等待毫秒(参数.轮询间隔毫秒 + 抖动时间);
1028
1030
  }
1029
- else {
1030
- const 错误信息 = `任务执行${状态}:${状态响应.error || 状态响应.message || '未知错误'}`;
1031
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 错误信息, { itemIndex: 参数.itemIndex });
1031
+ 首次 = false;
1032
+ const 状态响应 = await 获取任务状态({
1033
+ taskId: 参数.taskId,
1034
+ 状态超时毫秒: 参数.状态超时毫秒,
1035
+ 重试: 参数.重试,
1036
+ 调试: 参数.调试,
1037
+ 并发闸门: 参数.并发闸门,
1038
+ });
1039
+ const 任务状态 = typeof 状态响应.status === 'string' ? 状态响应.status.toLowerCase() : '';
1040
+ const 终端状态列表 = ['finished', 'failed', 'canceled', 'cancelled', 'error'];
1041
+ if (终端状态列表.includes(任务状态)) {
1042
+ if (任务状态 === 'finished') {
1043
+ 脚本模块.校验脚本是否失败(状态响应, 参数.itemIndex, 参数.finished但error算失败);
1044
+ }
1045
+ else {
1046
+ const 错误信息 = `任务执行${任务状态}:` +
1047
+ (String((_b = (_a = 状态响应.error) !== null && _a !== void 0 ? _a : 状态响应.message) !== null && _b !== void 0 ? _b : '未知错误'));
1048
+ throw new n8n_workflow_1.NodeOperationError(节点, 错误信息, { itemIndex: 参数.itemIndex });
1049
+ }
1050
+ if (参数.调试)
1051
+ 参数.调试['轮询次数'] = (轮询次数 + 1);
1052
+ return 状态响应;
1032
1053
  }
1033
- if (参数.调试)
1034
- 参数.调试['轮询次数'] = (轮询次数 + 1);
1035
- return 状态响应;
1054
+ 轮询次数++;
1036
1055
  }
1037
- 轮询次数++;
1038
- }
1039
- if (参数.调试)
1040
- 参数.调试['轮询次数'] = 轮询次数;
1041
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `异步任务等待超时(${Math.round(参数.总等待超时毫秒 / 1000)}秒)`, {
1042
- itemIndex: 参数.itemIndex,
1043
- });
1044
- };
1045
- const 格式化输出 = (响应数据, 索引, opt, 附加, operation) => {
1046
- var _a, _b, _c, _d, _e;
1047
- const data = ((_a = 响应数据.data) !== null && _a !== void 0 ? _a : undefined);
1048
- if (opt.g_parseResultString && data && typeof data.result === 'string') {
1049
- try {
1050
- data.result = JSON.parse(data.result);
1056
+ if (参数.调试)
1057
+ 参数.调试['轮询次数'] = 轮询次数;
1058
+ throw new n8n_workflow_1.NodeOperationError(节点, `异步任务等待超时(${Math.round(参数.总等待超时毫秒 / 1000)}秒)`, { itemIndex: 参数.itemIndex });
1059
+ };
1060
+ return { 调用脚本接口, 获取任务状态, 等待异步任务完成 };
1061
+ })();
1062
+ const 输出模块 = (() => {
1063
+ const 格式化输出 = (响应数据, 索引, opt, 附加, operation) => {
1064
+ var _a, _b, _c, _d, _e;
1065
+ const 数据内容 = ((_a = 响应数据.data) !== null && _a !== void 0 ? _a : undefined);
1066
+ if (opt.g_parseResultString && 数据内容 && typeof 数据内容.result === 'string') {
1067
+ try {
1068
+ 数据内容.result = JSON.parse(数据内容.result);
1069
+ }
1070
+ catch {
1071
+ 数据内容.result = 数据内容.result.replace(/\\n/g, '\n');
1072
+ }
1051
1073
  }
1052
- catch {
1053
- data.result = data.result.replace(/\\n/g, '\n');
1074
+ const 元数据 = { operation: operation || 'unknown' };
1075
+ const 数据中的任务ID = 数据内容 === null || 数据内容 === void 0 ? void 0 : 数据内容.task_id;
1076
+ const 响应中的任务ID = 响应数据.task_id;
1077
+ if (数据中的任务ID)
1078
+ 元数据.task_id = 数据中的任务ID;
1079
+ else if (响应中的任务ID)
1080
+ 元数据.task_id = 响应中的任务ID;
1081
+ const 批次索引 = (_b = 附加 === null || 附加 === void 0 ? void 0 : 附加.批次索引) !== null && _b !== void 0 ? _b : 附加 === null || 附加 === void 0 ? void 0 : 附加.batchIndex;
1082
+ if (批次索引 !== undefined)
1083
+ 元数据.batchIndex = 批次索引;
1084
+ const 输出列表 = [];
1085
+ if (opt.f_outputFormat === 'resultOnly') {
1086
+ 输出列表.push({
1087
+ json: {
1088
+ ...(附加 !== null && 附加 !== void 0 ? 附加 : {}),
1089
+ _meta: 元数据,
1090
+ result: (_c = 数据内容 === null || 数据内容 === void 0 ? void 0 : 数据内容.result) !== null && _c !== void 0 ? _c : null,
1091
+ status: ((_d = 响应数据.status) !== null && _d !== void 0 ? _d : null),
1092
+ error: ((_e = 响应数据.error) !== null && _e !== void 0 ? _e : ''),
1093
+ },
1094
+ pairedItem: { item: 索引 },
1095
+ });
1096
+ return 输出列表;
1054
1097
  }
1055
- }
1056
- const _meta = { operation: operation || 'unknown' };
1057
- const sheetNameFromData = (data === null || data === void 0 ? void 0 : data.sheet_name) || (data === null || data === void 0 ? void 0 : data.sheetName);
1058
- const sheetNameFromResponse = 是IDataObject(响应数据) ? (响应数据.sheet_name || 响应数据.sheetName) : undefined;
1059
- const sheetNameFromArgv = 是IDataObject(附加) && 是IDataObject(附加.argv) ? (附加.argv.sheet_name || 附加.argv.sheetName) : undefined;
1060
- if (sheetNameFromData)
1061
- _meta.sheet_name = sheetNameFromData;
1062
- else if (sheetNameFromResponse)
1063
- _meta.sheet_name = sheetNameFromResponse;
1064
- else if (sheetNameFromArgv)
1065
- _meta.sheet_name = sheetNameFromArgv;
1066
- const taskIdFromData = data === null || data === void 0 ? void 0 : data.task_id;
1067
- const taskIdFromResponse = 响应数据.task_id;
1068
- if (taskIdFromData)
1069
- _meta.task_id = taskIdFromData;
1070
- else if (taskIdFromResponse)
1071
- _meta.task_id = taskIdFromResponse;
1072
- const batchIndex = (_b = 附加 === null || 附加 === void 0 ? void 0 : 附加.批次索引) !== null && _b !== void 0 ? _b : 附加 === null || 附加 === void 0 ? void 0 : 附加.batchIndex;
1073
- if (batchIndex !== undefined)
1074
- _meta.batchIndex = batchIndex;
1075
- const out = [];
1076
- if (opt.f_outputFormat === 'resultOnly') {
1077
- out.push({
1078
- json: {
1079
- ...(附加 !== null && 附加 !== void 0 ? 附加 : {}),
1080
- _meta,
1081
- result: (_c = data === null || data === void 0 ? void 0 : data.result) !== null && _c !== void 0 ? _c : null,
1082
- status: ((_d = 响应数据.status) !== null && _d !== void 0 ? _d : null),
1083
- error: ((_e = 响应数据.error) !== null && _e !== void 0 ? _e : ''),
1084
- },
1085
- pairedItem: { item: 索引 },
1086
- });
1087
- return out;
1088
- }
1089
- if (opt.f_outputFormat === 'splitLogs') {
1090
- const logs = data === null || data === void 0 ? void 0 : data.logs;
1091
- if (Array.isArray(logs)) {
1092
- for (const 日志条目 of logs) {
1093
- if (日志条目 && typeof 日志条目 === 'object') {
1094
- out.push({ json: { ...(附加 !== null && 附加 !== void 0 ? 附加 : {}), _meta, ...日志条目 }, pairedItem: { item: 索引 } });
1098
+ if (opt.f_outputFormat === 'splitLogs') {
1099
+ const 日志列表 = 数据内容 === null || 数据内容 === void 0 ? void 0 : 数据内容.logs;
1100
+ if (Array.isArray(日志列表) && 日志列表.length > 0) {
1101
+ for (const 日志条目 of 日志列表) {
1102
+ if (日志条目 && typeof 日志条目 === 'object') {
1103
+ 输出列表.push({
1104
+ json: { ...(附加 !== null && 附加 !== void 0 ? 附加 : {}), _meta: 元数据, ...日志条目 },
1105
+ pairedItem: { item: 索引 },
1106
+ });
1107
+ }
1095
1108
  }
1109
+ return 输出列表;
1096
1110
  }
1111
+ return [
1112
+ {
1113
+ json: { ...(附加 !== null && 附加 !== void 0 ? 附加 : {}), _meta: 元数据, logs: [] },
1114
+ pairedItem: { item: 索引 },
1115
+ },
1116
+ ];
1097
1117
  }
1098
- return out;
1099
- }
1100
- if (opt.f_outputFormat === 'splitErrors') {
1101
- const logs = data === null || data === void 0 ? void 0 : data.logs;
1102
- if (Array.isArray(logs)) {
1103
- for (const 日志条目 of logs) {
1104
- if (日志条目 && typeof 日志条目 === 'object') {
1105
- const level = 日志条目.level;
1106
- if (level === 'error')
1107
- out.push({ json: { ...(附加 !== null && 附加 !== void 0 ? 附加 : {}), _meta, ...日志条目 }, pairedItem: { item: 索引 } });
1118
+ if (opt.f_outputFormat === 'splitErrors') {
1119
+ const 日志列表 = 数据内容 === null || 数据内容 === void 0 ? void 0 : 数据内容.logs;
1120
+ if (Array.isArray(日志列表) && 日志列表.length > 0) {
1121
+ for (const 日志条目 of 日志列表) {
1122
+ if (日志条目 && typeof 日志条目 === 'object') {
1123
+ const 日志级别 = 日志条目.level;
1124
+ if (日志级别 === 'error') {
1125
+ 输出列表.push({
1126
+ json: { ...(附加 !== null && 附加 !== void 0 ? 附加 : {}), _meta: 元数据, ...日志条目 },
1127
+ pairedItem: { item: 索引 },
1128
+ });
1129
+ }
1130
+ }
1108
1131
  }
1132
+ return 输出列表.length > 0
1133
+ ? 输出列表
1134
+ : [
1135
+ {
1136
+ json: { ...(附加 !== null && 附加 !== void 0 ? 附加 : {}), _meta: 元数据, errors: [] },
1137
+ pairedItem: { item: 索引 },
1138
+ },
1139
+ ];
1109
1140
  }
1141
+ return [
1142
+ {
1143
+ json: { ...(附加 !== null && 附加 !== void 0 ? 附加 : {}), _meta: 元数据, errors: [] },
1144
+ pairedItem: { item: 索引 },
1145
+ },
1146
+ ];
1110
1147
  }
1111
- return out;
1112
- }
1113
- out.push({ json: { ...(附加 !== null && 附加 !== void 0 ? 附加 : {}), _meta, ...响应数据 }, pairedItem: { item: 索引 } });
1114
- return out;
1115
- };
1116
- const 处理单个项目 = async (索引, 并发闸门) => {
1117
- const 操作类型 = this.getNodeParameter('operation', 索引, 'runScriptSync');
1118
- const opt = 读取高级选项(索引);
1119
- const 重试 = {
1120
- 最大重试次数: opt.j_maxRetries,
1121
- 初始等待毫秒: opt.k_retryInitialDelayMs,
1122
- 退避倍数: opt.l_retryBackoffFactor,
1123
- 尊重RetryAfter: opt.m_honorRetryAfter,
1148
+ 输出列表.push({
1149
+ json: { ...(附加 !== null && 附加 !== void 0 ? 附加 : {}), _meta: 元数据, ...响应数据 },
1150
+ pairedItem: { item: 索引 },
1151
+ });
1152
+ return 输出列表;
1124
1153
  };
1125
- const 调试字段 = opt.w_debug ? {} : undefined;
1126
- if (操作类型 === 'getTaskStatus') {
1127
- const taskId = this.getNodeParameter('taskId', 索引, '');
1128
- if (!taskId || !taskId.trim()) {
1129
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Task ID 不能为空', { itemIndex: 索引 });
1154
+ return { 格式化输出 };
1155
+ })();
1156
+ const 调度模块 = (() => {
1157
+ const opt0 = 选项模块.读取高级选项(0);
1158
+ const 全局并发闸门 = 创建并发闸门(Math.max(1, opt0.x_globalMaxConcurrency));
1159
+ const operation0 = this.getNodeParameter('operation', 0, 'runScriptSync');
1160
+ let indicesToRun = [];
1161
+ if (operation0 === 'getTaskStatus') {
1162
+ indicesToRun = Array.from({ length: 输入数据项列表.length }, (_, i) => i);
1163
+ }
1164
+ else {
1165
+ indicesToRun = [0];
1166
+ if (opt0.y_dangerEachItemEnabled) {
1167
+ indicesToRun = Array.from({ length: 输入数据项列表.length }, (_, i) => i);
1168
+ if (indicesToRun.length > 1 && !opt0.z_confirmDangerEachItem) {
1169
+ throw new n8n_workflow_1.NodeOperationError(节点, `你开启了「危险:每个输入项执行一次」。当前输入共有 ${indicesToRun.length} 项,将会调用 AirScript ${indicesToRun.length} 次,极易造成重复写入。\n` +
1170
+ `如确实需要,请在高级选项中勾选「危险模式:确认多次执行」。否则请关闭危险模式(推荐)。`, { itemIndex: 0 });
1171
+ }
1130
1172
  }
1131
- const 状态响应 = await 获取任务状态({
1132
- taskId,
1133
- 状态超时毫秒: opt.i_statusTimeoutSeconds * 1000,
1134
- 重试,
1135
- 调试: 调试字段,
1136
- 并发闸门,
1137
- });
1138
- 校验脚本是否失败(状态响应, 索引, opt.n_failOnScriptError);
1139
- const 附加 = opt.w_debug ? { _调试: 调试字段 } : undefined;
1140
- return 格式化输出(状态响应, 索引, opt, 附加, 操作类型);
1141
1173
  }
1142
- const { 文件ID, 脚本ID, argv参数, 上下文 } = 解析执行信息(索引, 输入数据项列表);
1174
+ return { opt0, 全局并发闸门, indicesToRun };
1175
+ })();
1176
+ const 业务模块 = (() => {
1143
1177
  const 组装批次失败响应 = (错误信息, 堆栈) => ({
1144
1178
  status: 'finished',
1145
1179
  error: 错误信息,
@@ -1151,167 +1185,223 @@ class KingsoftAirscript {
1151
1185
  },
1152
1186
  data: { result: null, logs: [] },
1153
1187
  });
1154
- const 执行一次 = async (上下文入参, 调试对象) => {
1155
- if (操作类型 === 'runScriptSync') {
1156
- const 响应 = await 调用脚本接口({
1157
- 类型: 'sync',
1158
- 文件ID,
1159
- 脚本ID,
1160
- 上下文: 上下文入参,
1161
- 请求超时毫秒: opt.h_requestTimeoutSeconds * 1000,
1162
- 重试,
1163
- 调试: 调试对象,
1164
- 并发闸门,
1165
- });
1166
- 校验脚本是否失败(响应, 索引, opt.n_failOnScriptError);
1167
- return 响应;
1188
+ const 处理单个项目 = async (索引, 并发闸门) => {
1189
+ var _a;
1190
+ const 操作类型 = this.getNodeParameter('operation', 索引, 'runScriptSync');
1191
+ const opt = 选项模块.读取高级选项(索引);
1192
+ const 重试配置 = 选项模块.创建重试配置(opt);
1193
+ const 调试字段 = opt.w_debug ? {} : undefined;
1194
+ if (opt.w_debug && 调试字段) {
1195
+ 调试字段['执行摘要'] = {
1196
+ operation: 操作类型,
1197
+ 输入item数量: 输入数据项列表.length,
1198
+ 危险每项执行: opt.y_dangerEachItemEnabled,
1199
+ 分批启用: opt.r_autoChunkEnabled,
1200
+ 分批字段路径: opt.s_chunkFieldPath,
1201
+ 每批条数: opt.t_chunkSize,
1202
+ 批次并发: opt.u_chunkMaxConcurrency,
1203
+ 分批输出模式: opt.v_chunkOutputMode,
1204
+ };
1168
1205
  }
1169
- if (操作类型 === 'runScriptAsync') {
1170
- const 初始响应 = await 调用脚本接口({
1171
- 类型: 'async',
1172
- 文件ID,
1173
- 脚本ID,
1174
- 上下文: 上下文入参,
1175
- 请求超时毫秒: opt.h_requestTimeoutSeconds * 1000,
1176
- 重试,
1177
- 调试: 调试对象,
1178
- 并发闸门,
1179
- });
1180
- if (!opt.c_waitForCompletion)
1181
- return 初始响应;
1182
- const taskId = 提取TaskId(初始响应);
1183
- if (!taskId) {
1184
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), '异步执行返回的响应中未找到 task_id(data.task_id 或 task_id)。', { itemIndex: 索引 });
1206
+ if (操作类型 === 'getTaskStatus') {
1207
+ const taskId = this.getNodeParameter('taskId', 索引, '');
1208
+ if (!taskId || !taskId.trim()) {
1209
+ throw new n8n_workflow_1.NodeOperationError(节点, 'Task ID 不能为空', { itemIndex: 索引 });
1185
1210
  }
1186
- return await 等待异步任务完成({
1187
- itemIndex: 索引,
1211
+ const 状态响应 = await 网络模块.获取任务状态({
1188
1212
  taskId,
1189
- 轮询间隔毫秒: opt.d_pollIntervalSeconds * 1000,
1190
- 轮询抖动毫秒: opt.o_pollJitterMs,
1191
- 总等待超时毫秒: opt.b_timeoutSeconds * 1000,
1192
1213
  状态超时毫秒: opt.i_statusTimeoutSeconds * 1000,
1193
- 重试,
1194
- finished但error算失败: opt.n_failOnScriptError,
1195
- 调试: 调试对象,
1214
+ 重试: 重试配置,
1215
+ 调试: 调试字段,
1196
1216
  并发闸门,
1217
+ itemIndex: 索引,
1197
1218
  });
1198
- }
1199
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `未知的操作类型: ${操作类型}`, { itemIndex: 索引 });
1200
- };
1201
- if (opt.r_autoChunkEnabled) {
1202
- const 数组路径 = opt.s_chunkFieldPath;
1203
- const 目标数组 = 取路径值(argv参数, 数组路径);
1204
- if (!Array.isArray(目标数组) || 目标数组.length === 0) {
1205
- const 响应 = await 执行一次(上下文, 调试字段);
1219
+ 脚本模块.校验脚本是否失败(状态响应, 索引, opt.n_failOnScriptError);
1206
1220
  const 附加 = opt.w_debug ? { _调试: 调试字段 } : undefined;
1207
- return 格式化输出(响应, 索引, opt, 附加, 操作类型);
1221
+ return 输出模块.格式化输出(状态响应, 索引, opt, 附加, 操作类型);
1208
1222
  }
1209
- const 批次大小 = Math.max(1, opt.t_chunkSize);
1210
- const 并发数 = Math.max(1, opt.u_chunkMaxConcurrency);
1211
- const 任务列表 = [];
1212
- for (let i = 0; i < 目标数组.length; i += 批次大小) {
1213
- const 批次 = 目标数组.slice(i, i + 批次大小);
1214
- const 批次索引 = Math.floor(i / 批次大小);
1215
- 任务列表.push(async () => {
1216
- try {
1217
- const 批次调试对象 = opt.w_debug ? {} : undefined;
1218
- const 批次上下文 = {
1219
- ...上下文,
1220
- argv: 以不可变方式写入路径(argv参数, 数组路径, 批次),
1221
- };
1222
- const 响应 = await 执行一次(批次上下文, 批次调试对象);
1223
- const 批次附加 = opt.w_debug
1224
- ? { 批次索引, _调试: { ...(批次调试对象 !== null && 批次调试对象 !== void 0 ? 批次调试对象 : {}) } }
1225
- : { 批次索引 };
1226
- return 格式化输出(响应, 索引, opt, 批次附加, 操作类型);
1223
+ const { 文件ID, 脚本ID } = 参数模块.解析文件脚本ID(索引);
1224
+ let argv参数;
1225
+ if (opt.y_dangerEachItemEnabled) {
1226
+ argv参数 = 参数模块.构建Argv_危险每项模式(索引, opt);
1227
+ }
1228
+ else {
1229
+ argv参数 = 参数模块.构建Argv_V3安全模式(0);
1230
+ }
1231
+ const 上下文 = 参数模块.合并Context(索引, argv参数, 调试字段);
1232
+ const 执行一次 = async (上下文入参, 调试对象) => {
1233
+ if (操作类型 === 'runScriptSync') {
1234
+ const 响应 = await 网络模块.调用脚本接口({
1235
+ 类型: 'sync',
1236
+ 文件ID,
1237
+ 脚本ID,
1238
+ 上下文: 上下文入参,
1239
+ 请求超时毫秒: opt.h_requestTimeoutSeconds * 1000,
1240
+ 重试: 重试配置,
1241
+ 调试: 调试对象,
1242
+ 并发闸门,
1243
+ });
1244
+ 脚本模块.校验脚本是否失败(响应, 索引, opt.n_failOnScriptError);
1245
+ return 响应;
1246
+ }
1247
+ if (操作类型 === 'runScriptAsync') {
1248
+ const 初始响应 = await 网络模块.调用脚本接口({
1249
+ 类型: 'async',
1250
+ 文件ID,
1251
+ 脚本ID,
1252
+ 上下文: 上下文入参,
1253
+ 请求超时毫秒: opt.h_requestTimeoutSeconds * 1000,
1254
+ 重试: 重试配置,
1255
+ 调试: 调试对象,
1256
+ 并发闸门,
1257
+ });
1258
+ if (!opt.c_waitForCompletion)
1259
+ return 初始响应;
1260
+ const taskId = 脚本模块.提取TaskId(初始响应);
1261
+ if (!taskId) {
1262
+ throw new n8n_workflow_1.NodeOperationError(节点, '异步执行返回的响应中未找到 task_id(data.task_id 或 task_id)。', { itemIndex: 索引 });
1227
1263
  }
1228
- catch (错误) {
1229
- if (!this.continueOnFail())
1230
- throw 错误;
1231
- const err = 错误 instanceof Error ? 错误 : new Error(String(错误));
1232
- const 错误对象 = 组装批次失败响应(err.message, err.stack);
1233
- const 错误附加 = opt.w_debug
1234
- ? { 批次索引, _调试: { 错误: err.message } }
1235
- : { 批次索引, 错误: err.message };
1236
- return 格式化输出(错误对象, 索引, opt, 错误附加, 操作类型);
1264
+ return await 网络模块.等待异步任务完成({
1265
+ itemIndex: 索引,
1266
+ taskId,
1267
+ 轮询间隔毫秒: opt.d_pollIntervalSeconds * 1000,
1268
+ 轮询抖动毫秒: opt.o_pollJitterMs,
1269
+ 总等待超时毫秒: opt.b_timeoutSeconds * 1000,
1270
+ 状态超时毫秒: opt.i_statusTimeoutSeconds * 1000,
1271
+ 重试: 重试配置,
1272
+ finished但error算失败: opt.n_failOnScriptError,
1273
+ 调试: 调试对象,
1274
+ 并发闸门,
1275
+ });
1276
+ }
1277
+ throw new n8n_workflow_1.NodeOperationError(节点, `未知的操作类型: ${操作类型}`, { itemIndex: 索引 });
1278
+ };
1279
+ if (opt.r_autoChunkEnabled) {
1280
+ let 数组路径 = opt.s_chunkFieldPath || 'data';
1281
+ let 目标数组 = 工具模块.取路径值(argv参数, 数组路径);
1282
+ const 执行模式 = this.getNodeParameter('runModeV3', 0, 'uiOnce');
1283
+ if ((!Array.isArray(目标数组) || 目标数组.length === 0) && 执行模式 === 'aggregateOnce') {
1284
+ const 聚合配置 = this.getNodeParameter('aggregate', 0, {});
1285
+ const 聚合字段路径 = String((_a = 聚合配置.targetArrayField) !== null && _a !== void 0 ? _a : 'data');
1286
+ if (聚合字段路径 && 聚合字段路径 !== 数组路径) {
1287
+ const 备选数组 = 工具模块.取路径值(argv参数, 聚合字段路径);
1288
+ if (Array.isArray(备选数组) && 备选数组.length > 0) {
1289
+ const 原数组路径 = 数组路径;
1290
+ 数组路径 = 聚合字段路径;
1291
+ 目标数组 = 备选数组;
1292
+ if (opt.w_debug && 调试字段) {
1293
+ 调试字段['分批字段路径自动切换'] = {
1294
+ 从: 原数组路径,
1295
+ 到: 数组路径,
1296
+ 原因: '原分批字段未找到数组,已回退到聚合字段',
1297
+ };
1298
+ }
1299
+ }
1237
1300
  }
1238
- });
1301
+ }
1302
+ if (!Array.isArray(目标数组) || 目标数组.length === 0) {
1303
+ const 响应 = await 执行一次(上下文, 调试字段);
1304
+ const 附加 = opt.w_debug ? { _调试: 调试字段 } : undefined;
1305
+ return 输出模块.格式化输出(响应, 索引, opt, 附加, 操作类型);
1306
+ }
1307
+ const 批次大小 = Math.max(1, opt.t_chunkSize);
1308
+ const 批次并发 = Math.max(1, opt.u_chunkMaxConcurrency);
1309
+ const 任务列表 = [];
1310
+ for (let i = 0; i < 目标数组.length; i += 批次大小) {
1311
+ const 批次 = 目标数组.slice(i, i + 批次大小);
1312
+ const 批次索引 = Math.floor(i / 批次大小);
1313
+ 任务列表.push(async () => {
1314
+ try {
1315
+ const 批次调试对象 = opt.w_debug ? {} : undefined;
1316
+ const 批次argv = 工具模块.以不可变方式写入路径(argv参数, 数组路径, 批次);
1317
+ const 批次上下文 = { ...上下文, argv: 批次argv };
1318
+ const 响应 = await 执行一次(批次上下文, 批次调试对象);
1319
+ const 批次附加 = opt.w_debug
1320
+ ? { 批次索引, _调试: { ...(批次调试对象 !== null && 批次调试对象 !== void 0 ? 批次调试对象 : {}) } }
1321
+ : { 批次索引 };
1322
+ return 输出模块.格式化输出(响应, 索引, opt, 批次附加, 操作类型);
1323
+ }
1324
+ catch (错误) {
1325
+ if (!this.continueOnFail())
1326
+ throw 错误;
1327
+ const err = 错误 instanceof Error ? 错误 : new Error(String(错误));
1328
+ const 错误对象 = 组装批次失败响应(err.message, err.stack);
1329
+ const 错误附加 = opt.w_debug
1330
+ ? { 批次索引, _调试: { 错误: err.message } }
1331
+ : { 批次索引, 错误: err.message };
1332
+ return 输出模块.格式化输出(错误对象, 索引, opt, 错误附加, 操作类型);
1333
+ }
1334
+ });
1335
+ }
1336
+ const 批次结果 = await 工具模块.并发执行(任务列表, 批次并发);
1337
+ if (opt.v_chunkOutputMode === 'merge') {
1338
+ const 批次输出 = 批次结果.flat();
1339
+ const 批次输出列表 = 批次输出.map((x) => x.json);
1340
+ const taskIds = 批次输出
1341
+ .map((x) => x.json)
1342
+ .filter((json) => typeof json === 'object' && json !== null)
1343
+ .map((json) => {
1344
+ var _a, _b, _c;
1345
+ const fromData = (_a = json.data) === null || _a === void 0 ? void 0 : _a.task_id;
1346
+ const fromTop = json.task_id;
1347
+ const fromMeta = (_b = json._meta) === null || _b === void 0 ? void 0 : _b.task_id;
1348
+ return (_c = fromData !== null && fromData !== void 0 ? fromData : fromTop) !== null && _c !== void 0 ? _c : fromMeta;
1349
+ })
1350
+ .filter((id) => typeof id === 'string' && id.length > 0);
1351
+ const 批次数 = Math.ceil(目标数组.length / 批次大小);
1352
+ const 分批汇总 = {
1353
+ 字段路径: 数组路径,
1354
+ 总数据条数: 目标数组.length,
1355
+ 每批条数: 批次大小,
1356
+ 批次数,
1357
+ 并发数: 批次并发,
1358
+ };
1359
+ const 合并结果 = {
1360
+ 分批汇总,
1361
+ 批次输出列表,
1362
+ ...(taskIds.length === 1 ? { task_id: taskIds[0] } : taskIds.length > 1 ? { task_ids: taskIds } : {}),
1363
+ ...(opt.w_debug ? { _调试: 调试字段 } : {}),
1364
+ };
1365
+ return [{ json: 合并结果, pairedItem: { item: 索引 } }];
1366
+ }
1367
+ return 批次结果.flat();
1239
1368
  }
1240
- const 批次结果 = await 并发执行(任务列表, 并发数);
1241
- if (opt.v_chunkOutputMode === 'merge') {
1242
- const 批次输出 = 批次结果.flat();
1243
- const 批次输出列表 = 批次输出.map(x => x.json);
1244
- const taskIds = 批次输出
1245
- .map(x => x.json)
1246
- .filter((json) => typeof json === 'object' && json !== null)
1247
- .map(json => {
1248
- var _a, _b, _c;
1249
- const fromData = (_a = json.data) === null || _a === void 0 ? void 0 : _a.task_id;
1250
- const fromTop = json.task_id;
1251
- const fromMeta = (_b = json._meta) === null || _b === void 0 ? void 0 : _b.task_id;
1252
- return (_c = fromData !== null && fromData !== void 0 ? fromData : fromTop) !== null && _c !== void 0 ? _c : fromMeta;
1253
- })
1254
- .filter((id) => typeof id === 'string' && id.length > 0);
1255
- const 批次数 = Math.ceil(目标数组.length / 批次大小);
1256
- const 分批汇总 = {
1257
- 批次数,
1258
- 每批条数: Array.from({ length: 批次数 }, (_, i) => {
1259
- const start = i * 批次大小;
1260
- const end = Math.min(start + 批次大小, 目标数组.length);
1261
- return end - start;
1262
- }),
1263
- 字段路径: 数组路径,
1264
- 总数据条数: 目标数组.length,
1265
- 并发数,
1266
- };
1267
- const 合并结果 = {
1268
- 分批汇总,
1269
- 批次输出列表,
1270
- ...(taskIds.length === 1 ? { task_id: taskIds[0] } : taskIds.length > 1 ? { task_ids: taskIds } : {}),
1271
- ...(opt.w_debug ? { _调试: 调试字段 } : {}),
1272
- };
1273
- return [{ json: 合并结果, pairedItem: { item: 索引 } }];
1369
+ const 响应 = await 执行一次(上下文, 调试字段);
1370
+ const 附加 = opt.w_debug ? { _调试: 调试字段 } : undefined;
1371
+ return 输出模块.格式化输出(响应, 索引, opt, 附加, 操作类型);
1372
+ };
1373
+ const 安全执行单个项目 = async (itemIndex, 并发闸门) => {
1374
+ var _a, _b, _c;
1375
+ try {
1376
+ return await 处理单个项目(itemIndex, 并发闸门);
1274
1377
  }
1275
- return 批次结果.flat();
1276
- }
1277
- const 响应 = await 执行一次(上下文, 调试字段);
1278
- const 附加 = opt.w_debug ? { _调试: 调试字段 } : undefined;
1279
- return 格式化输出(响应, 索引, opt, 附加, 操作类型);
1280
- };
1281
- const 安全执行单个项目 = async (itemIndex, 并发闸门) => {
1282
- var _a, _b, _c;
1283
- try {
1284
- return await 处理单个项目(itemIndex, 并发闸门);
1285
- }
1286
- catch (e) {
1287
- if (!this.continueOnFail())
1288
- throw e;
1289
- const err = 提取Http错误(e);
1290
- return [
1291
- {
1292
- json: {
1293
- error: 提取错误消息(err),
1294
- code: (_a = err.code) !== null && _a !== void 0 ? _a : null,
1295
- statusCode: (_b = getStatusCode(err)) !== null && _b !== void 0 ? _b : null,
1296
- details: (_c = getBody(err)) !== null && _c !== void 0 ? _c : null,
1378
+ catch (e) {
1379
+ if (!this.continueOnFail())
1380
+ throw e;
1381
+ const 实际错误 = 错误模块.提取Http错误(e);
1382
+ return [
1383
+ {
1384
+ json: {
1385
+ error: 错误模块.提取错误消息(实际错误),
1386
+ code: (_a = 实际错误.code) !== null && _a !== void 0 ? _a : null,
1387
+ statusCode: (_b = 错误模块.获取状态码(实际错误)) !== null && _b !== void 0 ? _b : null,
1388
+ details: (_c = 错误模块.获取响应体(实际错误)) !== null && _c !== void 0 ? _c : null,
1389
+ },
1390
+ pairedItem: { item: itemIndex },
1297
1391
  },
1298
- pairedItem: { item: itemIndex },
1299
- },
1300
- ];
1301
- }
1302
- };
1303
- const operation0 = this.getNodeParameter('operation', 0, 'runScriptSync');
1304
- const payloadMode0 = this.getNodeParameter('payloadMode', 0, 'argvFromUI');
1305
- const appendOnce = (operation0 === 'runScriptSync' || operation0 === 'runScriptAsync')
1306
- && payloadMode0 === 'appendItemsToArrayField';
1307
- const indicesToRun = appendOnce ? [0] : Array.from({ length: 输入数据项列表.length }, (_, i) => i);
1392
+ ];
1393
+ }
1394
+ };
1395
+ return { 安全执行单个项目 };
1396
+ })();
1308
1397
  const 汇总输出 = [];
1398
+ const { opt0, 全局并发闸门, indicesToRun } = 调度模块;
1309
1399
  if (opt0.a_parallelExecution) {
1310
1400
  const 批处理数量 = Math.max(1, opt0.e_batchSize);
1311
1401
  for (let i = 0; i < indicesToRun.length; i += 批处理数量) {
1312
1402
  const slice = indicesToRun.slice(i, i + 批处理数量);
1313
- const 任务函数列表 = slice.map((idx) => () => 安全执行单个项目(idx, 全局并发闸门));
1314
- const 批次结果 = await 并发执行(任务函数列表, slice.length);
1403
+ const 任务函数列表 = slice.map((idx) => () => 业务模块.安全执行单个项目(idx, 全局并发闸门));
1404
+ const 批次结果 = await 工具模块.并发执行(任务函数列表, slice.length);
1315
1405
  for (const 单项结果 of 批次结果) {
1316
1406
  汇总输出.push(...单项结果);
1317
1407
  }
@@ -1319,7 +1409,7 @@ class KingsoftAirscript {
1319
1409
  }
1320
1410
  else {
1321
1411
  for (const idx of indicesToRun) {
1322
- const 单项结果 = await 安全执行单个项目(idx, 全局并发闸门);
1412
+ const 单项结果 = await 业务模块.安全执行单个项目(idx, 全局并发闸门);
1323
1413
  汇总输出.push(...单项结果);
1324
1414
  }
1325
1415
  }