digitalsee-ai-flow-cli 0.2.1 → 0.6.15-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.github/workflows/publish.yml +1 -0
  2. package/README.md +285 -17
  3. package/dist/api/link.d.ts.map +1 -1
  4. package/dist/api/link.js +4 -0
  5. package/dist/api/link.js.map +1 -1
  6. package/dist/api/mapping.d.ts.map +1 -0
  7. package/dist/api/mapping.js +95 -0
  8. package/dist/api/mapping.js.map +1 -0
  9. package/dist/api/node.d.ts.map +1 -1
  10. package/dist/api/node.js +4 -0
  11. package/dist/api/node.js.map +1 -1
  12. package/dist/api/org.d.ts.map +1 -0
  13. package/dist/api/org.js +21 -0
  14. package/dist/api/org.js.map +1 -0
  15. package/dist/commands/flow/list.d.ts.map +1 -1
  16. package/dist/commands/flow/list.js +13 -1
  17. package/dist/commands/flow/list.js.map +1 -1
  18. package/dist/commands/flow/mapping.d.ts.map +1 -0
  19. package/dist/commands/flow/mapping.js +734 -0
  20. package/dist/commands/flow/mapping.js.map +1 -0
  21. package/dist/commands/flow/node.d.ts.map +1 -1
  22. package/dist/commands/flow/node.js +108 -8
  23. package/dist/commands/flow/node.js.map +1 -1
  24. package/dist/commands/flow/test.d.ts.map +1 -1
  25. package/dist/commands/flow/test.js +0 -30
  26. package/dist/commands/flow/test.js.map +1 -1
  27. package/dist/commands/flow/update.d.ts.map +1 -1
  28. package/dist/commands/flow/update.js +31 -5
  29. package/dist/commands/flow/update.js.map +1 -1
  30. package/dist/commands/flow.d.ts.map +1 -1
  31. package/dist/commands/flow.js +2 -0
  32. package/dist/commands/flow.js.map +1 -1
  33. package/dist/commands/link/account-linking.d.ts.map +1 -0
  34. package/dist/commands/link/account-linking.js +123 -0
  35. package/dist/commands/link/account-linking.js.map +1 -0
  36. package/dist/commands/link/check-mapping.d.ts.map +1 -0
  37. package/dist/commands/link/check-mapping.js +37 -0
  38. package/dist/commands/link/check-mapping.js.map +1 -0
  39. package/dist/commands/link/create.d.ts.map +1 -1
  40. package/dist/commands/link/create.js +22 -4
  41. package/dist/commands/link/create.js.map +1 -1
  42. package/dist/commands/link.d.ts.map +1 -1
  43. package/dist/commands/link.js +4 -0
  44. package/dist/commands/link.js.map +1 -1
  45. package/dist/commands/org.d.ts.map +1 -0
  46. package/dist/commands/org.js +248 -0
  47. package/dist/commands/org.js.map +1 -0
  48. package/dist/commands/sync/check-mapping.d.ts.map +1 -0
  49. package/dist/commands/sync/check-mapping.js +37 -0
  50. package/dist/commands/sync/check-mapping.js.map +1 -0
  51. package/dist/commands/sync/create.d.ts.map +1 -1
  52. package/dist/commands/sync/create.js +22 -4
  53. package/dist/commands/sync/create.js.map +1 -1
  54. package/dist/commands/sync.d.ts.map +1 -1
  55. package/dist/commands/sync.js +2 -0
  56. package/dist/commands/sync.js.map +1 -1
  57. package/dist/index.js +5 -3
  58. package/dist/index.js.map +1 -1
  59. package/dist/types/index.d.ts.map +1 -1
  60. package/dist/types/org.d.ts.map +1 -0
  61. package/dist/types/org.js +5 -0
  62. package/dist/types/org.js.map +1 -0
  63. package/dist/utils/format.d.ts.map +1 -1
  64. package/dist/utils/format.js +23 -0
  65. package/dist/utils/format.js.map +1 -1
  66. package/dist/utils/output.d.ts.map +1 -1
  67. package/dist/utils/output.js +4 -0
  68. package/dist/utils/output.js.map +1 -1
  69. package/dist/utils/updateCheck.d.ts.map +1 -1
  70. package/dist/utils/updateCheck.js +9 -0
  71. package/dist/utils/updateCheck.js.map +1 -1
  72. package/package.json +1 -1
  73. package/dist/api/dip.d.ts.map +0 -1
  74. package/dist/api/dip.js +0 -14
  75. package/dist/api/dip.js.map +0 -1
@@ -0,0 +1,734 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isMappingSupported = isMappingSupported;
4
+ exports.getDirectionFromBusinessType = getDirectionFromBusinessType;
5
+ exports.getAvailableSides = getAvailableSides;
6
+ exports.findMappingByName = findMappingByName;
7
+ exports.getDirectionLabel = getDirectionLabel;
8
+ exports.getMappingSubPath = getMappingSubPath;
9
+ exports.buildMappingEntry = buildMappingEntry;
10
+ exports.formatMappingMethod = formatMappingMethod;
11
+ exports.filterProfileMappingData = filterProfileMappingData;
12
+ exports.filterBySide = filterBySide;
13
+ exports.registerFlowMapping = registerFlowMapping;
14
+ const output_1 = require("@/utils/output");
15
+ const flow_1 = require("@/api/flow");
16
+ const mapping_1 = require("@/api/mapping");
17
+ const MAPPING_BUSINESS_TYPES = [5, 6];
18
+ function isMappingSupported(flow) {
19
+ return flow.business_type !== undefined && MAPPING_BUSINESS_TYPES.includes(flow.business_type);
20
+ }
21
+ async function validateFlowSupportsMapping(flowId) {
22
+ const flow = await (0, flow_1.getFlowDetail)(flowId);
23
+ if (!isMappingSupported(flow)) {
24
+ const msg = `该连接流不支持属性映射(business_type=${flow.business_type ?? '未知'}),仅通讯录集成(business_type=5)和通讯录同步(business_type=6)支持`;
25
+ throw new Error(msg);
26
+ }
27
+ return flow.business_type;
28
+ }
29
+ const ENTITY_FIELD_MAP = {
30
+ user: {
31
+ label: '用户',
32
+ digParams: 'digUserSubParams',
33
+ digName: 'digUserName',
34
+ otherParams: 'otherUserSubParams',
35
+ otherName: 'otherUserName',
36
+ mappingToIam: 'otherUserMappingToIam',
37
+ mappingToApp: 'otherUserMappingToApp',
38
+ },
39
+ org: {
40
+ label: '部门',
41
+ digParams: 'digOrgSubParams',
42
+ digName: 'digOrgName',
43
+ otherParams: 'otherOrgSubParams',
44
+ otherName: 'otherOrgName',
45
+ mappingToIam: 'otherOrgMappingToIam',
46
+ mappingToApp: 'otherOrgMappingToApp',
47
+ },
48
+ role: {
49
+ label: '角色',
50
+ digParams: 'digRoleSubParams',
51
+ digName: 'digRoleName',
52
+ otherParams: 'otherRoleSubParams',
53
+ otherName: 'otherRoleName',
54
+ mappingToIam: 'otherRoleMappingToIam',
55
+ mappingToApp: 'otherRoleMappingToApp',
56
+ },
57
+ };
58
+ // ── Validation helpers ──────────────────────────────────────
59
+ const VALID_ENTITIES = ['user', 'org', 'role'];
60
+ function validateEntity(entity) {
61
+ if (entity !== undefined && !VALID_ENTITIES.includes(entity)) {
62
+ throw new Error(`无效的实体类型: ${entity},可选值: user | org | role`);
63
+ }
64
+ }
65
+ const VALID_SIDES = ['upstream', 'idaas', 'downstream'];
66
+ function validateSide(side) {
67
+ if (side !== undefined && !VALID_SIDES.includes(side)) {
68
+ throw new Error(`无效的端: ${side},可选值: upstream | idaas | downstream`);
69
+ }
70
+ }
71
+ const VALID_DIRECTIONS = ['all', 'upstream', 'downstream'];
72
+ function validateDirection(direction) {
73
+ if (!VALID_DIRECTIONS.includes(direction)) {
74
+ throw new Error(`无效的方向: ${direction},可选值: upstream | downstream | all`);
75
+ }
76
+ }
77
+ // ── Business type / direction utilities ─────────────────────
78
+ function getDirectionFromBusinessType(businessType) {
79
+ if (businessType === 5)
80
+ return 'upstream';
81
+ if (businessType === 6)
82
+ return 'downstream';
83
+ throw new Error(`不支持的 business_type: ${businessType}`);
84
+ }
85
+ function getAvailableSides(businessType) {
86
+ if (businessType === 5)
87
+ return ['upstream', 'idaas'];
88
+ if (businessType === 6)
89
+ return ['idaas', 'downstream'];
90
+ return ['upstream', 'idaas', 'downstream'];
91
+ }
92
+ function findMappingByName(subParams, name, direction) {
93
+ if (direction === 'upstream') {
94
+ return subParams.findIndex((item) => item.name === name);
95
+ }
96
+ return subParams.findIndex((item) => item.value === name);
97
+ }
98
+ function getDirectionLabel(direction) {
99
+ return direction === 'upstream' ? '上游 → IDAAS' : 'IDAAS → 下游';
100
+ }
101
+ function getMappingSubPath(entity, direction) {
102
+ const mappingKey = `${entity}_mapping`;
103
+ const directionKey = direction === 'upstream' ? 'to_iam' : 'to_app';
104
+ return [mappingKey, directionKey, 'sub_params'];
105
+ }
106
+ function ensureSubParams(data, entity, direction) {
107
+ const [mappingKey, directionKey] = getMappingSubPath(entity, direction);
108
+ let mapping = data[mappingKey];
109
+ if (!mapping) {
110
+ mapping = {};
111
+ data[mappingKey] = mapping;
112
+ }
113
+ let dirData = mapping[directionKey];
114
+ if (!dirData) {
115
+ dirData = {};
116
+ mapping[directionKey] = dirData;
117
+ }
118
+ let arr = dirData.sub_params;
119
+ if (!arr) {
120
+ arr = [];
121
+ dirData.sub_params = arr;
122
+ }
123
+ return arr;
124
+ }
125
+ function getSubParams(data, entity, direction) {
126
+ const [mappingKey, directionKey] = getMappingSubPath(entity, direction);
127
+ const mapping = data[mappingKey];
128
+ const dirData = mapping?.[directionKey];
129
+ return dirData?.sub_params || [];
130
+ }
131
+ function buildMappingEntry(sourceField, targetField, method, direction, expression, targetExpression) {
132
+ const update = method === 'update';
133
+ const base = direction === 'upstream'
134
+ ? { name: targetField, value: sourceField, update }
135
+ : { name: sourceField, value: targetField, update };
136
+ if (expression) {
137
+ base.source_exp = expression;
138
+ base.exp_type = 'JS_EXP';
139
+ }
140
+ if (targetExpression) {
141
+ base.target_exp = targetExpression;
142
+ base.exp_type = 'JS_EXP';
143
+ }
144
+ return base;
145
+ }
146
+ function formatMappingMethod(item) {
147
+ return item.update ? 'update' : 'create';
148
+ }
149
+ function filterProfileMappingData(data, entity, direction) {
150
+ const result = { ...data };
151
+ if (entity && entity !== 'user') {
152
+ result.digUserSubParams = [];
153
+ result.otherUserSubParams = [];
154
+ result.otherUserMappingToIam = [];
155
+ result.otherUserMappingToApp = [];
156
+ }
157
+ if (entity && entity !== 'org') {
158
+ result.digOrgSubParams = [];
159
+ result.otherOrgSubParams = [];
160
+ result.otherOrgMappingToIam = [];
161
+ result.otherOrgMappingToApp = [];
162
+ }
163
+ if (entity && entity !== 'role') {
164
+ result.digRoleSubParams = [];
165
+ result.otherRoleSubParams = [];
166
+ result.otherRoleMappingToIam = [];
167
+ result.otherRoleMappingToApp = [];
168
+ }
169
+ if (direction === 'upstream') {
170
+ result.otherUserMappingToApp = [];
171
+ result.otherOrgMappingToApp = [];
172
+ result.otherRoleMappingToApp = [];
173
+ }
174
+ else if (direction === 'downstream') {
175
+ result.otherUserMappingToIam = [];
176
+ result.otherOrgMappingToIam = [];
177
+ result.otherRoleMappingToIam = [];
178
+ }
179
+ return result;
180
+ }
181
+ function filterBySide(data, side, entity) {
182
+ const result = filterProfileMappingData(data, entity);
183
+ if (side === 'idaas') {
184
+ result.otherUserSubParams = [];
185
+ result.otherOrgSubParams = [];
186
+ result.otherRoleSubParams = [];
187
+ result.otherUserMappingToIam = [];
188
+ result.otherUserMappingToApp = [];
189
+ result.otherOrgMappingToIam = [];
190
+ result.otherOrgMappingToApp = [];
191
+ result.otherRoleMappingToIam = [];
192
+ result.otherRoleMappingToApp = [];
193
+ }
194
+ else if (side === 'upstream') {
195
+ result.otherUserMappingToApp = [];
196
+ result.otherOrgMappingToApp = [];
197
+ result.otherRoleMappingToApp = [];
198
+ result.digUserSubParams = [];
199
+ result.digOrgSubParams = [];
200
+ result.digRoleSubParams = [];
201
+ }
202
+ else if (side === 'downstream') {
203
+ result.otherUserMappingToIam = [];
204
+ result.otherOrgMappingToIam = [];
205
+ result.otherRoleMappingToIam = [];
206
+ result.otherUserSubParams = [];
207
+ result.otherOrgSubParams = [];
208
+ result.otherRoleSubParams = [];
209
+ }
210
+ return result;
211
+ }
212
+ // ── JSON / rendering utilities ──────────────────────────────
213
+ const JSON_FIELD_WHITELIST = new Set([
214
+ 'name',
215
+ 'display_name',
216
+ 'type',
217
+ 'value_type',
218
+ 'value',
219
+ 'update',
220
+ 'source_exp',
221
+ 'target_exp',
222
+ 'exp_type',
223
+ ]);
224
+ function stripMappingItem(item) {
225
+ const out = { name: item.name };
226
+ for (const key of JSON_FIELD_WHITELIST) {
227
+ if (key !== 'name' && item[key] !== undefined && item[key] !== null) {
228
+ out[key] = item[key];
229
+ }
230
+ }
231
+ return out;
232
+ }
233
+ function stripMappingArray(items) {
234
+ return items.map(stripMappingItem);
235
+ }
236
+ function formatSourceValue(item) {
237
+ if (item.exp_type === 'JS_EXP' && item.source_exp) {
238
+ return `JS: ${item.source_exp}`;
239
+ }
240
+ return String(item.value || '-');
241
+ }
242
+ function renderMappingTable(mappings, title, direction) {
243
+ if (mappings.length === 0) {
244
+ (0, output_1.printWarning)(`${title}: 无映射`);
245
+ return;
246
+ }
247
+ (0, output_1.printSubHeader)(title);
248
+ const headers = direction === 'upstream'
249
+ ? ['上游字段', 'IDAAS 字段', '显示名', '类型', '方式']
250
+ : ['下游字段', '显示名', 'IDAAS 字段', '类型', '方式'];
251
+ const rows = mappings.map((m) => {
252
+ const idaasLabel = m.exp_type === 'JS_EXP' && m.target_exp
253
+ ? `JS: ${m.target_exp}`
254
+ : direction === 'upstream'
255
+ ? String(m.name || '-')
256
+ : String(m.value || '-');
257
+ if (direction === 'upstream') {
258
+ return [
259
+ formatSourceValue(m),
260
+ idaasLabel,
261
+ String(m.display_name || '-'),
262
+ String(m.value_type || m.type || '-'),
263
+ formatMappingMethod(m),
264
+ ];
265
+ }
266
+ return [
267
+ formatSourceValue(m),
268
+ String(m.display_name || '-'),
269
+ idaasLabel,
270
+ String(m.value_type || m.type || '-'),
271
+ formatMappingMethod(m),
272
+ ];
273
+ });
274
+ (0, output_1.printTable)(headers, rows);
275
+ }
276
+ function renderFieldOptions(fields, title) {
277
+ if (fields.length === 0) {
278
+ (0, output_1.printWarning)(`${title}: 无可用字段`);
279
+ return;
280
+ }
281
+ (0, output_1.printSubHeader)(`${title} (${fields.length} 个字段)`);
282
+ const headers = ['字段名', '显示名', '类型'];
283
+ const rows = fields.map((f) => [
284
+ f.name || '-',
285
+ f.display_name || f.name || '-',
286
+ String(f.value_type || f.type || '-'),
287
+ ]);
288
+ (0, output_1.printTable)(headers, rows);
289
+ }
290
+ function resolveRefType(explicitType, businessType) {
291
+ if (explicitType)
292
+ return explicitType;
293
+ return businessType === 6 ? 'PUSHCONNECTOR' : 'CONNECTOR';
294
+ }
295
+ function wrapAction(errorLabel) {
296
+ return (fn) => async (flowId, options) => {
297
+ try {
298
+ if (options.json)
299
+ (0, output_1.setJsonMode)(true);
300
+ await fn(flowId, options);
301
+ }
302
+ catch (error) {
303
+ if (options.json) {
304
+ (0, output_1.printStructuredResult)({ ok: false, message: error.message });
305
+ }
306
+ else {
307
+ (0, output_1.printError)(`${errorLabel}失败: ${error.message}`);
308
+ }
309
+ }
310
+ };
311
+ }
312
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
313
+ function parseShowOptions(options) {
314
+ const refType = options.type;
315
+ const mode = options.mode || 'all';
316
+ const entity = options.entity;
317
+ const side = options.side;
318
+ const direction = side ? 'all' : (options.direction || 'all');
319
+ if (side && options.direction && options.direction !== 'all') {
320
+ throw new Error('--side 与 --direction 互斥,不能同时使用');
321
+ }
322
+ validateEntity(entity);
323
+ validateSide(side);
324
+ validateDirection(direction);
325
+ return { refType, mode, entity, side, direction };
326
+ }
327
+ async function prepareShowData(flowId, params) {
328
+ const businessType = await validateFlowSupportsMapping(flowId);
329
+ const refType = resolveRefType(params.refType, businessType);
330
+ const availableSides = getAvailableSides(businessType);
331
+ if (params.side && !availableSides.includes(params.side)) {
332
+ const btLabel = businessType === 5 ? '通讯录集成' : '通讯录同步';
333
+ throw new Error(`该流为${btLabel},不支持 --side ${params.side},可用: ${availableSides.join(' | ')}`);
334
+ }
335
+ // 按需获取:避免请求不需要的数据
336
+ const needsDig = params.mode !== 'configured' || params.side === 'idaas';
337
+ const needsConnector = params.side !== 'idaas';
338
+ const [dig, raw] = await Promise.all([
339
+ needsDig ? (0, mapping_1.getDigProfileTemplates)(refType) : null,
340
+ needsConnector ? (0, mapping_1.fetchProfileData)(flowId, refType) : null,
341
+ ]);
342
+ const data = (0, mapping_1.buildProfileMappingData)(dig, raw);
343
+ // Auto-filter based on flow type when no --side specified
344
+ if (!params.side) {
345
+ if (businessType === 5) {
346
+ data.otherUserMappingToApp = [];
347
+ data.otherOrgMappingToApp = [];
348
+ data.otherRoleMappingToApp = [];
349
+ }
350
+ else if (businessType === 6) {
351
+ data.otherUserMappingToIam = [];
352
+ data.otherOrgMappingToIam = [];
353
+ data.otherRoleMappingToIam = [];
354
+ data.otherUserSubParams = [];
355
+ data.otherOrgSubParams = [];
356
+ data.otherRoleSubParams = [];
357
+ }
358
+ }
359
+ const filtered = params.side
360
+ ? filterBySide(data, params.side, params.entity)
361
+ : filterProfileMappingData(data, params.entity, params.direction);
362
+ return { filtered, businessType };
363
+ }
364
+ function printShowJsonOutput(filtered, mode) {
365
+ const jsonData = {};
366
+ const showAvailable = mode === 'all' || mode === 'available';
367
+ const showConfigured = mode === 'all' || mode === 'configured';
368
+ if (showAvailable) {
369
+ for (const fm of Object.values(ENTITY_FIELD_MAP)) {
370
+ const dig = filtered[fm.digParams];
371
+ const other = filtered[fm.otherParams];
372
+ if (dig.length > 0) {
373
+ jsonData[fm.digParams] = stripMappingArray(dig);
374
+ jsonData[fm.digName] = filtered[fm.digName];
375
+ }
376
+ if (other.length > 0) {
377
+ jsonData[fm.otherParams] = stripMappingArray(other);
378
+ jsonData[fm.otherName] = filtered[fm.otherName];
379
+ }
380
+ }
381
+ }
382
+ if (showConfigured) {
383
+ for (const fm of Object.values(ENTITY_FIELD_MAP)) {
384
+ for (const key of [fm.mappingToIam, fm.mappingToApp]) {
385
+ const arr = filtered[key];
386
+ if (arr.length > 0)
387
+ jsonData[key] = stripMappingArray(arr);
388
+ }
389
+ }
390
+ }
391
+ (0, output_1.printJson)(jsonData);
392
+ }
393
+ // ── Show command rendering helpers ──────────────────────────
394
+ function entityMatches(filter, entityType) {
395
+ return !filter || filter === entityType;
396
+ }
397
+ function renderIdaaAvailableFields(filtered, entity) {
398
+ for (const [et, fm] of Object.entries(ENTITY_FIELD_MAP)) {
399
+ if (!entityMatches(entity, et))
400
+ continue;
401
+ renderFieldOptions(filtered[fm.digParams], `IDAAS ${fm.label}字段: ${filtered[fm.digName]}`);
402
+ }
403
+ }
404
+ function renderConfiguredMappings(filtered, side, entity, direction) {
405
+ if (side === 'idaas') {
406
+ (0, output_1.printWarning)('IDAAS 端无已配置映射(IDAAS 端仅包含字段模板,使用 --mode available 查看)');
407
+ return;
408
+ }
409
+ if (side === 'upstream') {
410
+ for (const [et, fm] of Object.entries(ENTITY_FIELD_MAP)) {
411
+ if (!entityMatches(entity, et))
412
+ continue;
413
+ const items = filtered[fm.mappingToIam];
414
+ if (items.length > 0)
415
+ renderMappingTable(items, `${fm.label}字段映射`, 'upstream');
416
+ }
417
+ return;
418
+ }
419
+ if (side === 'downstream') {
420
+ for (const [et, fm] of Object.entries(ENTITY_FIELD_MAP)) {
421
+ if (!entityMatches(entity, et))
422
+ continue;
423
+ const items = filtered[fm.mappingToApp];
424
+ if (items.length > 0)
425
+ renderMappingTable(items, `${fm.label}字段映射`, 'downstream');
426
+ }
427
+ return;
428
+ }
429
+ // No side filter: render both upstream and downstream sections
430
+ const showUpstream = direction === 'all' || direction === 'upstream';
431
+ const showDownstream = direction === 'all' || direction === 'downstream';
432
+ const hasUpstream = showUpstream &&
433
+ Object.entries(ENTITY_FIELD_MAP).some(([et, fm]) => entityMatches(entity, et) &&
434
+ filtered[fm.mappingToIam].length > 0);
435
+ const hasDownstream = showDownstream &&
436
+ Object.entries(ENTITY_FIELD_MAP).some(([et, fm]) => entityMatches(entity, et) &&
437
+ filtered[fm.mappingToApp].length > 0);
438
+ if (!hasUpstream && !hasDownstream) {
439
+ (0, output_1.printWarning)('暂无已配置的映射');
440
+ }
441
+ if (hasUpstream) {
442
+ (0, output_1.printHeader)('上游 → IDAAS 映射');
443
+ for (const [et, fm] of Object.entries(ENTITY_FIELD_MAP)) {
444
+ if (!entityMatches(entity, et))
445
+ continue;
446
+ const items = filtered[fm.mappingToIam];
447
+ if (items.length > 0)
448
+ renderMappingTable(items, `${fm.label}字段映射`, 'upstream');
449
+ }
450
+ }
451
+ if (hasDownstream) {
452
+ (0, output_1.printHeader)('IDAAS → 下游 映射');
453
+ for (const [et, fm] of Object.entries(ENTITY_FIELD_MAP)) {
454
+ if (!entityMatches(entity, et))
455
+ continue;
456
+ const items = filtered[fm.mappingToApp];
457
+ if (items.length > 0)
458
+ renderMappingTable(items, `${fm.label}字段映射`, 'downstream');
459
+ }
460
+ }
461
+ }
462
+ function renderAvailableFields(filtered, entity, isFocused, mode, side) {
463
+ if (isFocused && mode === 'all')
464
+ return;
465
+ (0, output_1.printHeader)('可用字段');
466
+ const digLabel = side === 'downstream' ? '下游' : 'IDAAS';
467
+ for (const [et, fm] of Object.entries(ENTITY_FIELD_MAP)) {
468
+ if (!entityMatches(entity, et))
469
+ continue;
470
+ const digFields = filtered[fm.digParams];
471
+ const otherFields = filtered[fm.otherParams];
472
+ if (digFields.length > 0) {
473
+ renderFieldOptions(digFields, `${digLabel} ${fm.label}字段: ${filtered[fm.digName]}`);
474
+ }
475
+ if (otherFields.length > 0) {
476
+ renderFieldOptions(otherFields, `上游${fm.label}字段: ${filtered[fm.otherName]}`);
477
+ }
478
+ }
479
+ }
480
+ // ── Command registration ────────────────────────────────────
481
+ function registerFlowMapping(flowCmd) {
482
+ const mappingCmd = flowCmd.command('mapping').description('属性映射管理(账号关联)');
483
+ // ── show ────────────────────────────────────────────────
484
+ mappingCmd
485
+ .command('show <flowId>')
486
+ .description('查看属性映射配置')
487
+ .option('--entity <entity>', '实体类型过滤: user | org | role')
488
+ .option('--side <side>', '聚焦端: upstream(上游端) | idaas(IDAAS端) | downstream(下游端)。与 --direction 互斥')
489
+ .option('--direction <direction>', '方向过滤: upstream(上游→IDAAS) | downstream(IDAAS→下游) | all(全部)', 'all')
490
+ .option('--mode <mode>', '显示模式: all(全部) | configured(仅已配置) | available(仅可配置项)', 'all')
491
+ .option('--json', '以 JSON 格式输出')
492
+ .action(wrapAction('获取映射配置')(async (flowId, options) => {
493
+ const params = parseShowOptions(options);
494
+ const { filtered } = await prepareShowData(flowId, params);
495
+ if (options.json) {
496
+ printShowJsonOutput(filtered, params.mode);
497
+ return;
498
+ }
499
+ // Title
500
+ const titleParts = [];
501
+ if (params.side) {
502
+ const sideLabels = {
503
+ upstream: '上游端',
504
+ idaas: 'IDAAS端',
505
+ downstream: '下游端',
506
+ };
507
+ titleParts.push(sideLabels[params.side]);
508
+ }
509
+ if (params.entity) {
510
+ const entityLabels = { user: '用户', org: '部门', role: '角色' };
511
+ titleParts.push(entityLabels[params.entity]);
512
+ }
513
+ const title = titleParts.length > 0
514
+ ? `${titleParts.join('')}映射配置 (Flow: ${flowId})`
515
+ : `属性映射配置 (Flow: ${flowId})`;
516
+ (0, output_1.printHeader)(title);
517
+ const isFocused = !!(params.entity && (params.side || params.direction !== 'all'));
518
+ if (params.side === 'idaas' && (params.mode === 'all' || params.mode === 'available')) {
519
+ renderIdaaAvailableFields(filtered, params.entity);
520
+ return;
521
+ }
522
+ if (params.mode === 'all' || params.mode === 'configured') {
523
+ renderConfiguredMappings(filtered, params.side, params.entity, params.direction);
524
+ }
525
+ if (params.mode === 'all' || params.mode === 'available') {
526
+ renderAvailableFields(filtered, params.entity, isFocused, params.mode, params.side);
527
+ }
528
+ }));
529
+ // ── add ─────────────────────────────────────────────────
530
+ mappingCmd
531
+ .command('add <flowId>')
532
+ .description('新增属性映射')
533
+ .requiredOption('--entity <entity>', '实体类型: user | org | role')
534
+ .option('--source-field <field>', '外部系统字段名(与 --expression 互斥)')
535
+ .option('--expression <expr>', 'source 侧 JS 表达式(与 --source-field 互斥)')
536
+ .option('--target-field <field>', 'IDAAS 字段名(与 --target-expression 互斥)')
537
+ .option('--target-expression <expr>', 'target 侧 JS 表达式(与 --target-field 互斥)')
538
+ .option('--method <method>', '映射方式: create | update', 'create')
539
+ .option('--type <refType>', '引用类型', 'CONNECTOR')
540
+ .option('--json', '以 JSON 格式输出')
541
+ .action(wrapAction('添加映射')(async (flowId, options) => {
542
+ const entity = options.entity;
543
+ validateEntity(entity);
544
+ if (options.sourceField && options.expression) {
545
+ throw new Error('--source-field 与 --expression 互斥,请只提供其中一个');
546
+ }
547
+ if (!options.sourceField && !options.expression) {
548
+ throw new Error('必须提供 --source-field 或 --expression');
549
+ }
550
+ if (options.targetField && options.targetExpression) {
551
+ throw new Error('--target-field 与 --target-expression 互斥,请只提供其中一个');
552
+ }
553
+ if (!options.targetField && !options.targetExpression) {
554
+ throw new Error('必须提供 --target-field 或 --target-expression');
555
+ }
556
+ const businessType = await validateFlowSupportsMapping(flowId);
557
+ const direction = getDirectionFromBusinessType(businessType);
558
+ const method = options.method === 'update' ? 'update' : 'create';
559
+ const refType = resolveRefType(options.type, businessType);
560
+ const profileData = await (0, mapping_1.fetchProfileData)(flowId, refType);
561
+ const subParams = ensureSubParams(profileData, entity, direction);
562
+ const idaasKey = options.targetField || options.targetExpression;
563
+ const existing = subParams.find((item) => direction === 'upstream' ? item.name === idaasKey : item.value === idaasKey);
564
+ if (existing && !options.targetExpression) {
565
+ throw new Error(`映射已存在: ${idaasKey},如需修改请使用 flow mapping set 命令`);
566
+ }
567
+ const sourceField = options.sourceField || options.expression;
568
+ const targetField = options.targetField || options.targetExpression;
569
+ const entry = buildMappingEntry(sourceField, targetField, method, direction, options.expression, options.targetExpression);
570
+ subParams.push(entry);
571
+ await (0, mapping_1.editMapping)(flowId, refType, profileData);
572
+ if (options.json) {
573
+ (0, output_1.printJson)({ ok: true, message: '映射已添加', entry, flowId });
574
+ }
575
+ else {
576
+ const sourceLabel = options.expression
577
+ ? `JS: ${options.expression}`
578
+ : options.sourceField;
579
+ const targetLabel = options.targetExpression
580
+ ? `JS: ${options.targetExpression}`
581
+ : options.targetField;
582
+ (0, output_1.printSuccess)(`映射已添加 (${getDirectionLabel(direction)}): ${sourceLabel} → ${targetLabel} (${method})`);
583
+ }
584
+ }));
585
+ // ── set ─────────────────────────────────────────────────
586
+ mappingCmd
587
+ .command('set <flowId>')
588
+ .description('修改已有映射')
589
+ .requiredOption('--entity <entity>', '实体类型: user | org | role')
590
+ .requiredOption('--name <fieldName>', 'IDAAS 字段名,用于定位映射条目')
591
+ .option('--source-field <field>', '新的外部系统字段名(与 --expression 互斥)')
592
+ .option('--expression <expr>', '新的 source 侧 JS 表达式(与 --source-field 互斥)')
593
+ .option('--target-field <field>', '新的 IDAAS 字段名(与 --target-expression 互斥)')
594
+ .option('--target-expression <expr>', '新的 target 侧 JS 表达式(与 --target-field 互斥)')
595
+ .option('--method <method>', '映射方式: create | update')
596
+ .option('--type <refType>', '引用类型', 'CONNECTOR')
597
+ .option('--json', '以 JSON 格式输出')
598
+ .action(wrapAction('修改映射')(async (flowId, options) => {
599
+ const entity = options.entity;
600
+ validateEntity(entity);
601
+ if (options.sourceField && options.expression) {
602
+ throw new Error('--source-field 与 --expression 互斥,请只提供其中一个');
603
+ }
604
+ if (options.targetField && options.targetExpression) {
605
+ throw new Error('--target-field 与 --target-expression 互斥,请只提供其中一个');
606
+ }
607
+ if (!options.sourceField &&
608
+ !options.expression &&
609
+ !options.targetField &&
610
+ !options.targetExpression &&
611
+ !options.method) {
612
+ throw new Error('至少需要提供一个修改项');
613
+ }
614
+ const businessType = await validateFlowSupportsMapping(flowId);
615
+ const direction = getDirectionFromBusinessType(businessType);
616
+ const refType = resolveRefType(options.type, businessType);
617
+ const profileData = await (0, mapping_1.fetchProfileData)(flowId, refType);
618
+ const subParams = getSubParams(profileData, entity, direction);
619
+ const idx = findMappingByName(subParams, options.name, direction);
620
+ if (idx === -1) {
621
+ throw new Error(`未找到映射: ${options.name}`);
622
+ }
623
+ const entry = subParams[idx];
624
+ const oldEntry = { ...entry };
625
+ if (options.sourceField !== undefined) {
626
+ if (direction === 'upstream') {
627
+ entry.value = options.sourceField;
628
+ }
629
+ else {
630
+ entry.name = options.sourceField;
631
+ }
632
+ delete entry.source_exp;
633
+ if (!entry.target_exp)
634
+ delete entry.exp_type;
635
+ }
636
+ if (options.expression !== undefined) {
637
+ if (direction === 'upstream') {
638
+ entry.value = options.expression;
639
+ }
640
+ else {
641
+ entry.name = options.expression;
642
+ }
643
+ entry.source_exp = options.expression;
644
+ entry.exp_type = 'JS_EXP';
645
+ }
646
+ if (options.targetField !== undefined) {
647
+ if (direction === 'upstream') {
648
+ entry.name = options.targetField;
649
+ }
650
+ else {
651
+ entry.value = options.targetField;
652
+ }
653
+ delete entry.target_exp;
654
+ if (!entry.source_exp)
655
+ delete entry.exp_type;
656
+ }
657
+ if (options.targetExpression !== undefined) {
658
+ if (direction === 'upstream') {
659
+ entry.name = options.targetExpression;
660
+ }
661
+ else {
662
+ entry.value = options.targetExpression;
663
+ }
664
+ entry.target_exp = options.targetExpression;
665
+ entry.exp_type = 'JS_EXP';
666
+ }
667
+ if (options.method !== undefined) {
668
+ entry.update = options.method === 'update';
669
+ }
670
+ subParams[idx] = entry;
671
+ await (0, mapping_1.editMapping)(flowId, refType, profileData);
672
+ if (options.json) {
673
+ (0, output_1.printJson)({ ok: true, message: '映射已更新', old: oldEntry, updated: entry, flowId });
674
+ }
675
+ else {
676
+ (0, output_1.printSuccess)(`映射已更新: ${options.name}`);
677
+ }
678
+ }));
679
+ // ── delete ──────────────────────────────────────────────
680
+ mappingCmd
681
+ .command('delete <flowId>')
682
+ .description('删除属性映射')
683
+ .requiredOption('--entity <entity>', '实体类型: user | org | role')
684
+ .requiredOption('--name <fieldName>', 'IDAAS 字段名,用于定位映射条目')
685
+ .option('--type <refType>', '引用类型', 'CONNECTOR')
686
+ .option('--json', '以 JSON 格式输出')
687
+ .action(wrapAction('删除映射')(async (flowId, options) => {
688
+ const entity = options.entity;
689
+ validateEntity(entity);
690
+ const businessType = await validateFlowSupportsMapping(flowId);
691
+ const direction = getDirectionFromBusinessType(businessType);
692
+ const refType = resolveRefType(options.type, businessType);
693
+ const profileData = await (0, mapping_1.fetchProfileData)(flowId, refType);
694
+ const subParams = getSubParams(profileData, entity, direction);
695
+ const beforeLen = subParams.length;
696
+ const newSubParams = subParams.filter((item) => direction === 'upstream' ? item.name !== options.name : item.value !== options.name);
697
+ if (newSubParams.length === beforeLen) {
698
+ throw new Error(`未找到映射: ${options.name}`);
699
+ }
700
+ const [mappingKey, directionKey] = getMappingSubPath(entity, direction);
701
+ const mapping = profileData[mappingKey];
702
+ const dirData = mapping?.[directionKey];
703
+ if (dirData) {
704
+ dirData.sub_params = newSubParams;
705
+ }
706
+ await (0, mapping_1.editMapping)(flowId, refType, profileData);
707
+ if (options.json) {
708
+ (0, output_1.printJson)({ ok: true, message: '映射已删除', name: options.name, flowId });
709
+ }
710
+ else {
711
+ (0, output_1.printSuccess)(`映射已删除: ${options.name}`);
712
+ }
713
+ }));
714
+ // ── check ───────────────────────────────────────────────
715
+ mappingCmd
716
+ .command('check <flowId>')
717
+ .description('检查映射是否已配置')
718
+ .option('--json', '以 JSON 格式输出')
719
+ .action(wrapAction('检查映射状态')(async (flowId, options) => {
720
+ await validateFlowSupportsMapping(flowId);
721
+ const result = await (0, mapping_1.checkMappingStatus)(flowId);
722
+ if (options.json) {
723
+ (0, output_1.printJson)({ ok: true, configured: result, flowId });
724
+ return;
725
+ }
726
+ if (result) {
727
+ (0, output_1.printSuccess)(`Flow ${flowId}: 映射已配置`);
728
+ }
729
+ else {
730
+ (0, output_1.printWarning)(`Flow ${flowId}: 映射未配置`);
731
+ }
732
+ }));
733
+ }
734
+ //# sourceMappingURL=mapping.js.map