@alipay/alipay-aipay 1.6.6 → 1.6.7-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 (26) hide show
  1. package/package.json +2 -2
  2. package/skill/VERSION +1 -1
  3. package/skill/references/integration/flow.md +22 -20
  4. package/skill/references/integration/modules/sandbox/a2m-sandbox-test.md +8 -29
  5. package/skill/references/integration/modules/scripts/sandbox_config.sh +2 -0
  6. package/skill/references/normal/full-process-routing.md +13 -11
  7. package/skill/references/normal/integration-worker.md +79 -0
  8. package/skill/references/normal/scripts/alipay_cli_env.mjs +0 -3
  9. package/skill/references/normal/scripts/alipay_cli_env.sh +0 -4
  10. package/skill/references/normal/scripts/common.sh +11 -0
  11. package/skill/references/normal/scripts/flow_loader.mjs +0 -19
  12. package/skill/references/normal/scripts/integration_message_runner.mjs +26 -53
  13. package/skill/references/normal/scripts/interaction_reply.mjs +2 -2
  14. package/skill/references/normal/scripts/onboarding_discovery_runner.mjs +4 -1
  15. package/skill/references/normal/scripts/onboarding_message_runner.mjs +33 -23
  16. package/skill/references/normal/scripts/onboarding_recovery_runner.mjs +2 -2
  17. package/skill/references/normal/scripts/render_customer_message.mjs +3 -3
  18. package/skill/references/normal/scripts/runtime.mjs +5 -23
  19. package/skill/references/normal/scripts/telemetry.mjs +368 -434
  20. package/skill/references/normal/telemetry.md +3 -4
  21. package/skill/references/onboarding/flow.md +14 -14
  22. package/skill/references/onboarding/modules/scripts/app.sh +1 -0
  23. package/skill/references/onboarding/modules/scripts/ar_sign_apply.sh +3 -1
  24. package/skill/references/onboarding/modules/scripts/auth.sh +4 -1
  25. package/skill/references/onboarding/modules/scripts/service.sh +2 -0
  26. package/skill/references/runtime-entry.md +12 -8
@@ -10,7 +10,7 @@ import { fileURLToPath } from 'node:url';
10
10
 
11
11
  import { isWindowsPlatform } from './platform_compat.mjs';
12
12
  import { renderMessage } from './render_customer_message.mjs';
13
- import { emitTerminalMetric, terminalMetricContextFromArgs } from './telemetry.mjs';
13
+ import { emitStageMetricAsync, startTelemetryRun } from './telemetry.mjs';
14
14
 
15
15
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
16
16
  const inspectorPath = path.join(scriptDir, 'project_route_inspector.sh');
@@ -373,32 +373,8 @@ function runUnixInspector(args, failureContext, jsonContext) {
373
373
  return parseSingleJson(inspected.stdout, jsonContext);
374
374
  }
375
375
 
376
- function defaultNewProjectPath() {
377
- const homeInput = process.env.HOME || os.homedir() || '';
378
- if (!homeInput) throw new Error('无法确定用户目录;请提供明确的新项目路径');
379
- const homePath = normalizeBasePath(homeInput);
380
- const container = path.join(homePath, 'alipay-aipay-projects');
381
- if (fs.existsSync(container)) {
382
- const stat = fs.lstatSync(container);
383
- if (!stat.isDirectory() || stat.isSymbolicLink()) {
384
- throw new Error('默认项目父目录不可用;请提供明确的新项目路径');
385
- }
386
- }
387
- for (let index = 1; index <= 50; index += 1) {
388
- const suffix = index === 1 ? '' : `-${index}`;
389
- const candidate = path.join(container, `pay-demo${suffix}`);
390
- if (!fs.existsSync(candidate)) return candidate;
391
- const stat = fs.lstatSync(candidate);
392
- if (stat.isDirectory() && !stat.isSymbolicLink() && !hasDirectoryEntries(candidate)) return candidate;
393
- }
394
- throw new Error('默认项目目录已被占用;请提供明确的新项目路径');
395
- }
396
-
397
- function parseNewProjectPath(input, targetPath) {
398
- if (targetPath !== undefined) return sanitizeProjectInput(targetPath);
399
- if (looksLikeExplicitPath(input)) return input;
400
- if (isDefaultNewProjectRequest(input)) return defaultNewProjectPath();
401
- throw new Error('自然语言新建请求必须由 Agent 解析后通过 --target-path 提供明确目标;禁止把用户原话当作目录名');
376
+ function parseNewProjectPath(projectPath) {
377
+ return sanitizeProjectInput(projectPath);
402
378
  }
403
379
 
404
380
  function normalizePotentialPath(candidate) {
@@ -830,13 +806,16 @@ function runResolveProject(args) {
830
806
  if (!['existing', 'new'].includes(intent)) throw new Error('intent 非法,仅支持 existing、new');
831
807
  const basePath = normalizeBasePath(required(args, 'base-path'));
832
808
  const input = sanitizeProjectInput(required(args, 'project-input'));
809
+ if (args['target-path'] !== undefined) {
810
+ throw new Error('参数 --target-path 已移除;现有项目和新建项目都使用 --project-path 传递真实路径');
811
+ }
812
+ const pathInput = sanitizeProjectInput(required(args, 'project-path'));
833
813
 
834
814
  if (intent === 'existing') {
835
- if (args['target-path'] !== undefined) throw new Error('--target-path 仅支持 --intent new');
836
815
  if (isExplicitNewProjectRequest(input)) {
837
816
  throw new Error('明确新建项目请求必须使用 --intent new,不能改用当前项目');
838
817
  }
839
- const existingInput = parseExistingProjectPath(input);
818
+ const existingInput = parseExistingProjectPath(pathInput);
840
819
  const candidate = currentProjectAliases.has(existingInput) ? basePath : resolveAgainstBase(existingInput, basePath);
841
820
  const projectPath = fs.existsSync(candidate) ? fs.realpathSync(candidate) : candidate;
842
821
  if (!fs.existsSync(projectPath) || !fs.statSync(projectPath).isDirectory()) {
@@ -846,7 +825,7 @@ function runResolveProject(args) {
846
825
  const prefix = currentProjectAliases.has(existingInput)
847
826
  ? '当前目录不是可识别的项目根,不能直接作为现有项目扫描'
848
827
  : '该路径未识别到项目根标记,不能确认是服务端项目根';
849
- const searchBasePath = currentProjectAliases.has(input) ? basePath : projectPath;
828
+ const searchBasePath = currentProjectAliases.has(existingInput) ? basePath : projectPath;
850
829
  throw projectRootError(searchBasePath, prefix, basePath);
851
830
  }
852
831
  return JSON.stringify({
@@ -857,7 +836,7 @@ function runResolveProject(args) {
857
836
  });
858
837
  }
859
838
 
860
- const newPathInput = parseNewProjectPath(input, args['target-path']);
839
+ const newPathInput = parseNewProjectPath(pathInput);
861
840
  if (currentProjectAliases.has(newPathInput)) {
862
841
  throw new Error('新项目必须明确尚不存在或为空的子目录名');
863
842
  }
@@ -1031,7 +1010,9 @@ function resolveFramework(args, projectPath, projectSelection, language) {
1031
1010
  ? sanitizeFramework(args.framework)
1032
1011
  : '';
1033
1012
  const suppliedSource = typeof args['framework-source'] === 'string' ? args['framework-source'] : '';
1034
- if (suppliedSource && !frameworkSources.has(suppliedSource)) throw new Error('framework-source 非法');
1013
+ if (suppliedSource && !frameworkSources.has(suppliedSource)) {
1014
+ throw new Error('framework-source 非法,仅支持 EXPLICIT、DEFAULT_NEW_PROJECT、PROJECT_INFERRED、PROJECT_STRUCTURE_FALLBACK');
1015
+ }
1035
1016
 
1036
1017
  if (!suppliedSource) {
1037
1018
  return suppliedFramework
@@ -1054,7 +1035,7 @@ function runFrameworkResolve(args) {
1054
1035
  const language = normalizeLanguage(required(args, 'language'));
1055
1036
  const projectSelection = required(args, 'project-selection');
1056
1037
  if (!['NEW_PROJECT', 'PREPARED_NEW_PROJECT', 'CURRENT_PROJECT', 'OTHER_PROJECT'].includes(projectSelection)) {
1057
- throw new Error('project-selection 非法');
1038
+ throw new Error('project-selection 非法,仅支持 CURRENT_PROJECT、OTHER_PROJECT、NEW_PROJECT、PREPARED_NEW_PROJECT');
1058
1039
  }
1059
1040
  let projectPath = '';
1060
1041
  if (projectSelection === 'CURRENT_PROJECT' || projectSelection === 'OTHER_PROJECT') {
@@ -1066,6 +1047,10 @@ function runFrameworkResolve(args) {
1066
1047
  }
1067
1048
 
1068
1049
  function startConfirmationFacts(args) {
1050
+ const rootIntent = required(args, 'root-intent');
1051
+ if (!['integration_only', 'full_process'].includes(rootIntent)) {
1052
+ throw new Error('root-intent 仅支持 integration_only、full_process');
1053
+ }
1069
1054
  const productType = required(args, 'product-type');
1070
1055
  const mapping = productVariants[productType];
1071
1056
  if (!mapping) throw new Error('product-type 非法,仅支持 aipay、webpay、apppay');
@@ -1098,6 +1083,7 @@ function startConfirmationFacts(args) {
1098
1083
  }
1099
1084
 
1100
1085
  return {
1086
+ rootIntent,
1101
1087
  productType,
1102
1088
  variant: mapping.variant,
1103
1089
  productName,
@@ -1123,8 +1109,10 @@ function renderStartConfirmation(facts) {
1123
1109
  });
1124
1110
  }
1125
1111
 
1126
- export function createStartConfirmation(args) {
1127
- return renderStartConfirmation(startConfirmationFacts(args));
1112
+ export async function createStartConfirmation(args) {
1113
+ const facts = startConfirmationFacts(args);
1114
+ await startTelemetryRun(facts.rootIntent, facts.productType);
1115
+ return renderStartConfirmation(facts);
1128
1116
  }
1129
1117
 
1130
1118
  export function createChecklistScopeReceipt(productType, projectPath) {
@@ -1231,7 +1219,7 @@ function runProductClarify() {
1231
1219
  function runProductUnsupported(args) {
1232
1220
  return renderMessage('product.unsupported', 'DEFAULT', {
1233
1221
  productName: required(args, 'product-name')
1234
- }, terminalMetricContextFromArgs(args, { terminalReason: 'unsupported_product' }));
1222
+ });
1235
1223
  }
1236
1224
 
1237
1225
  function runWindowsSandboxSetup() {
@@ -1313,12 +1301,6 @@ function checklistInputFromArgs(args) {
1313
1301
  return input;
1314
1302
  }
1315
1303
 
1316
- function terminalReasonForChecklist(input) {
1317
- if (input.overallResult === '通过') return 'completed';
1318
- if (input.overallResult === '未通过' || input.blockingDefectState === 'PRESENT') return 'integration_blocked';
1319
- return 'partial_with_remaining_actions';
1320
- }
1321
-
1322
1304
  async function runChecklistResult(args) {
1323
1305
  const variant = required(args, 'variant');
1324
1306
  if (!['AIPAY', 'WEBPAY', 'APPPAY'].includes(variant)) throw new Error('variant 非法,仅支持 AIPAY、WEBPAY、APPPAY');
@@ -1361,17 +1343,8 @@ async function runChecklistResult(args) {
1361
1343
  input.nextFlowReminder = '代码开发校验通过后将自动进入支付产品-产品开通;未通过时停留在代码开发。';
1362
1344
  }
1363
1345
  }
1346
+ emitStageMetricAsync('integration.complete_coding', { productType: variant.toLowerCase() });
1364
1347
  const rendered = renderMessage('integration.checklist.result', variant, input);
1365
- if (input.executionMode === '仅代码开发') {
1366
- emitTerminalMetric({
1367
- rootIntent: 'integration_only',
1368
- productType: variant,
1369
- ...terminalMetricContextFromArgs(args, {
1370
- rootIntent: 'integration_only',
1371
- terminalReason: terminalReasonForChecklist(input)
1372
- })
1373
- });
1374
- }
1375
1348
  return rendered;
1376
1349
  }
1377
1350
 
@@ -1386,7 +1359,7 @@ export async function runIntegrationMessageCli(argv = process.argv) {
1386
1359
  return;
1387
1360
  }
1388
1361
  if (command === 'start-confirm') {
1389
- console.log(createStartConfirmation(args));
1362
+ console.log(await createStartConfirmation(args));
1390
1363
  process.stderr.write(`${internalPrefix}CUSTOMER_MESSAGE_DELIVERY_REQUIRED=integration.start.confirm\n`);
1391
1364
  return;
1392
1365
  }
@@ -4,7 +4,7 @@ import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import process from 'node:process';
6
6
  import { fileURLToPath } from 'node:url';
7
- import { recordTelemetryUserInputForCurrentPhase } from './telemetry.mjs';
7
+ import { recordTelemetryUserInput } from './telemetry.mjs';
8
8
 
9
9
  const internalPrefix = 'ALIPAY_AIPAY_INTERNAL:';
10
10
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
@@ -125,7 +125,7 @@ export function runInteractionReplyCli(argv = process.argv) {
125
125
  if (command !== 'confirm' || typeof args.reply !== 'string' || !args.reply) {
126
126
  throw new Error('用法: interaction_reply.mjs confirm --reply <用户原始回复> --input-id <本地用户输入幂等键>');
127
127
  }
128
- recordTelemetryUserInputForCurrentPhase(required(args, 'input-id'));
128
+ recordTelemetryUserInput(required(args, 'input-id'));
129
129
  const decision = isAffirmativeReply(args.reply) ? 'CONFIRMED' : 'REJECTED';
130
130
  process.stderr.write(`${internalPrefix}REPLY_DECISION=${decision}\n`);
131
131
  if (decision === 'REJECTED') process.exitCode = 2;
@@ -293,7 +293,6 @@ async function runDiscovery(args) {
293
293
  const salesCode = required(args, 'sales-code');
294
294
  if (salesCode !== productSalesCodes[productType]) throw new Error('sales-code 与 product-type 不匹配');
295
295
  const reusedSigning = reusedSigningBranch(args, productType);
296
- emitStageMetricAsync('onboarding.discovery.start', { productType });
297
296
 
298
297
  const signingPromise = reusedSigning
299
298
  ? Promise.resolve(null)
@@ -324,6 +323,10 @@ async function runDiscovery(args) {
324
323
  : { query_status: 'NOT_APPLICABLE', applicable: false },
325
324
  application: applicationBranch(applicationResult)
326
325
  };
326
+ if (envelope.signing.query_status === 'SUCCESS'
327
+ && (envelope.signing.sign_status === 'SIGNED_EFFECTIVE' || envelope.signing.sign_status === 'SIGN_SUBMITTED')) {
328
+ emitStageMetricAsync('onboarding.confirm_signing', { productType });
329
+ }
327
330
  if (globalBlocker) {
328
331
  envelope.global_blocker = globalBlocker;
329
332
  envelope.auth_recovery_action = recoveryAction;
@@ -13,9 +13,7 @@ import {
13
13
  } from './render_customer_message.mjs';
14
14
  import {
15
15
  currentRunContext,
16
- emitStageMetricAsync,
17
- emitTerminalMetric,
18
- terminalMetricContextFromArgs
16
+ startTelemetryRun,
19
17
  } from './telemetry.mjs';
20
18
 
21
19
  const internalPrefix = 'ALIPAY_AIPAY_INTERNAL:';
@@ -159,12 +157,31 @@ async function readStdin() {
159
157
  function runPlatformEntry(args = {}) {
160
158
  if (!isWindowsPlatform()) return '';
161
159
  const runContext = currentRunContext();
162
- if (!runContext || runContext.terminal || !['onboarding_only', 'full_process'].includes(runContext.root_intent)) {
160
+ if (runContext?.terminal) {
161
+ throw new Error('Windows 产品开通引导缺少有效的当前 Onboarding run context');
162
+ }
163
+ const rootIntent = runContext?.root_intent || process.env.ALIPAY_AIPAY_ROOT_INTENT || process.env.ROOT_INTENT || '';
164
+ if (!['onboarding_only', 'full_process'].includes(rootIntent)) {
165
+ throw new Error('Windows 产品开通引导缺少有效的当前 Onboarding run context');
166
+ }
167
+ const platformId = runContext?.run_id
168
+ || platformIdFromEnv(process.env)
169
+ || '';
170
+ if (!platformId) {
163
171
  throw new Error('Windows 产品开通引导缺少有效的当前 Onboarding run context');
164
172
  }
165
173
  return renderMessage('onboarding.windows.redirect', 'DEFAULT', {
166
- officialUrl: buildWindowsOnboardingRedirectUrl(runContext.run_id)
167
- }, terminalMetricContextFromArgs(args, { terminalReason: 'unsupported_platform' }));
174
+ officialUrl: buildWindowsOnboardingRedirectUrl(platformId)
175
+ });
176
+ }
177
+
178
+ function platformIdFromEnv(env = process.env) {
179
+ const candidate = env.COZE_PROJECT_ID
180
+ || env.MEOO_PROJECT_ID
181
+ || (env.MIAODA ? env.VITE_APP_ID || env.PLATFORM_ID || '' : '');
182
+ return typeof candidate === 'string' && candidate && !/[\u0000-\u001f\u007f,^=]/u.test(candidate)
183
+ ? candidate
184
+ : '';
168
185
  }
169
186
 
170
187
  function parseJsonArg(args, name, label) {
@@ -188,15 +205,13 @@ function runEnterpriseUnsupported(args) {
188
205
  return renderMessage(
189
206
  'onboarding.enterprise.unsupported',
190
207
  variant,
191
- {},
192
- terminalMetricContextFromArgs(args, { terminalReason: 'enterprise_account_unsupported' })
208
+ {}
193
209
  );
194
210
  }
195
211
 
196
212
  async function runMccClarify(args) {
197
213
  const productType = required(args, 'product-type');
198
214
  const product = productFor({ ...args, 'product-type': productType });
199
- emitStageMetricAsync('onboarding.mcc_prepare.start', { productType });
200
215
  return renderMessage('onboarding.mcc.clarify', 'DEFAULT', {
201
216
  productName: product.productName
202
217
  }, { productType });
@@ -693,8 +708,13 @@ function requireCustomerMessageDelivery(messageId, message) {
693
708
  }
694
709
 
695
710
  async function runPlanConfirm(args) {
711
+ const rootIntent = required(args, 'root-intent');
712
+ if (!['onboarding_only', 'full_process'].includes(rootIntent)) {
713
+ throw new Error('root-intent 仅支持 onboarding_only、full_process');
714
+ }
696
715
  const productType = required(args, 'product-type');
697
716
  productFor({ ...args, 'product-type': productType });
717
+ await startTelemetryRun(rootIntent, productType);
698
718
  const mccRunContext = captureMccRunContext();
699
719
  return requireCustomerMessageDelivery('onboarding.plan.confirm', renderPlanConfirm(productType, {
700
720
  mccCode: required(args, 'mcc-code'),
@@ -707,8 +727,11 @@ async function runPlanConfirm(args) {
707
727
  async function runMccResolvePlan(args) {
708
728
  const productType = required(args, 'product-type');
709
729
  productFor({ ...args, 'product-type': productType });
730
+ const rootIntent = currentRunContext(process.env)?.root_intent || process.env.ALIPAY_AIPAY_ROOT_INTENT || process.env.ROOT_INTENT || '';
731
+ if (['onboarding_only', 'full_process'].includes(rootIntent)) {
732
+ await startTelemetryRun(rootIntent, productType);
733
+ }
710
734
  const mccRunContext = captureMccRunContext();
711
- emitStageMetricAsync('onboarding.mcc_prepare.start', { productType, runId: mccRunContext.runId });
712
735
  const query = required(args, 'query');
713
736
  const selected = optional(args, 'select');
714
737
  const selectionReceipt = optional(args, 'selection-receipt');
@@ -1117,19 +1140,6 @@ function closeoutInputFromArgs(args) {
1117
1140
  async function runCloseout(args) {
1118
1141
  const variables = closeoutInputFromArgs(args);
1119
1142
  const rendered = renderMessage('process.partial_result', 'DEFAULT', variables);
1120
- const terminalReason = args['root-intent'] === 'full_process' && variables.integrationResult !== '已完成'
1121
- ? 'partial_with_remaining_actions'
1122
- : variables.processResultTitle === '支付产品-产品开通已完成'
1123
- ? 'completed'
1124
- : 'partial_with_remaining_actions';
1125
- emitTerminalMetric({
1126
- productName: variables.productName,
1127
- rootIntent: args['root-intent'],
1128
- ...terminalMetricContextFromArgs(args, {
1129
- rootIntent: args['root-intent'],
1130
- terminalReason
1131
- })
1132
- });
1133
1143
  return rendered;
1134
1144
  }
1135
1145
 
@@ -5,7 +5,7 @@ import path from 'node:path';
5
5
  import process from 'node:process';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { isAffirmativeReply } from './interaction_reply.mjs';
8
- import { recordTelemetryUserInputForCurrentPhase } from './telemetry.mjs';
8
+ import { recordTelemetryUserInput } from './telemetry.mjs';
9
9
 
10
10
  const internalPrefix = 'ALIPAY_AIPAY_INTERNAL:';
11
11
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
@@ -34,7 +34,7 @@ function required(args, name) {
34
34
  }
35
35
 
36
36
  function assertRegisteredReply(args, kind) {
37
- recordTelemetryUserInputForCurrentPhase(required(args, 'input-id'));
37
+ recordTelemetryUserInput(required(args, 'input-id'));
38
38
  if (!isAffirmativeReply(required(args, 'reply'))) {
39
39
  process.stderr.write(`${internalPrefix}RECOVERY_INPUT_REJECTED=${kind}\n`);
40
40
  process.exitCode = 2;
@@ -238,7 +238,7 @@ function expectedMccRunIdFromContext(context = {}) {
238
238
 
239
239
  function assertCurrentMccRun(receiptPayload, env = process.env, expectedRunId = '') {
240
240
  const currentRunId = normalizeExpectedMccRunId(expectedRunId) || activeMccRunId(env);
241
- if (currentRunId && receiptPayload.run_id !== currentRunId) {
241
+ if (receiptPayload.run_id && receiptPayload.run_id !== currentRunId) {
242
242
  throw new Error('MCC 上下文回执不属于当前 run');
243
243
  }
244
244
  }
@@ -904,7 +904,7 @@ const managedEntrypoints = {
904
904
  reason: 'runner 按真实平台输出。'
905
905
  },
906
906
  'integration.start.confirm': {
907
- command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message start-confirm --product-type <aipay|webpay|apppay> --project-path <absPath> --project-selection <CURRENT_PROJECT|OTHER_PROJECT|PREPARED_NEW_PROJECT> --language <language> --framework <framework>',
907
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" integration-message start-confirm --root-intent <integration_only|full_process> --product-type <aipay|webpay|apppay> --project-path <absPath> --project-selection <CURRENT_PROJECT|OTHER_PROJECT|PREPARED_NEW_PROJECT> --language <language> --framework <framework>',
908
908
  reason: '绑定同次项目扫描事实。'
909
909
  },
910
910
  'integration.checklist.result': {
@@ -932,7 +932,7 @@ const managedEntrypoints = {
932
932
  reason: 'runner 执行发现并输出摘要。'
933
933
  },
934
934
  'onboarding.plan.confirm': {
935
- command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" mcc resolve-plan --query <经营描述|MCC编码> --product-type <aipay|webpay|apppay>,或已持有唯一动态 MCC 回执时使用 onboarding-message plan-confirm --product-type <...> --mcc-code <...> --mcc-name <...> --mcc-qualification <...> --mcc-context-receipt <...>',
935
+ command: 'node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" mcc resolve-plan --query <经营描述|MCC编码> --product-type <aipay|webpay|apppay>,或已持有唯一动态 MCC 回执时使用 onboarding-message plan-confirm --root-intent <onboarding_only|full_process> --product-type <...> --mcc-code <...> --mcc-name <...> --mcc-qualification <...> --mcc-context-receipt <...>',
936
936
  reason: 'mcc.sh 绑定当前产品支持类目、详情和特殊资质;plan-confirm 只用于已验证唯一事实。'
937
937
  },
938
938
  'materials.category.collect': {
@@ -5,6 +5,8 @@ import path from 'node:path';
5
5
  import process from 'node:process';
6
6
  import { fileURLToPath } from 'node:url';
7
7
 
8
+ import { startTelemetryRun } from './telemetry.mjs';
9
+
8
10
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
9
11
  const referencesDir = path.resolve(scriptDir, '../..');
10
12
  const flowDirs = Object.freeze({
@@ -128,25 +130,12 @@ function assertEnumOption(options, name, allowedValues, { required = true } = {}
128
130
  return value;
129
131
  }
130
132
 
131
- function appendOptionalTerminalCountOptions(targetArgs, options) {
132
- for (const name of ['user-input-count-total', 'user-input-count-after-flow-start', 'flow-start-observed']) {
133
- if (Object.prototype.hasOwnProperty.call(options, name)) targetArgs.push(`--${name}`, requiredOption(options, name));
134
- }
135
- }
136
-
137
- async function startTelemetryRun(rootIntent, productType, inputId = '') {
138
- const telemetryArgs = ['start-run', '--root-intent', rootIntent];
139
- if (productType) telemetryArgs.push('--product-type', productType);
140
- if (inputId) telemetryArgs.push('--input-id', inputId);
141
- const { runTelemetryCli } = await import('./telemetry.mjs');
142
- await runTelemetryCli(cliArgv(telemetryArgs));
143
- }
144
-
145
133
  async function runIntegrationBootstrap(args) {
146
134
  const options = parseRuntimeArgs(args);
147
135
  assertOnlyOptions(options, ['root-intent', 'product-type', 'input-id']);
148
136
  const rootIntent = assertEnumOption(options, 'root-intent', ['integration_only']);
149
137
  const productType = assertEnumOption(options, 'product-type', ['aipay', 'webpay', 'apppay'], { required: false });
138
+ process.env.ALIPAY_AIPAY_ROOT_INTENT = rootIntent;
150
139
  await startTelemetryRun(rootIntent, productType, options['input-id']);
151
140
  const { runFlowCli } = await import('./flow_loader.mjs');
152
141
  runFlowCli(cliArgv(['integration', 'entry']));
@@ -154,20 +143,13 @@ async function runIntegrationBootstrap(args) {
154
143
 
155
144
  async function runOnboardingBootstrap(args) {
156
145
  const options = parseRuntimeArgs(args);
157
- assertOnlyOptions(options, [
158
- 'root-intent',
159
- 'product-type',
160
- 'input-id',
161
- 'user-input-count-total',
162
- 'user-input-count-after-flow-start',
163
- 'flow-start-observed'
164
- ]);
146
+ assertOnlyOptions(options, ['root-intent', 'product-type', 'input-id']);
165
147
  const rootIntent = assertEnumOption(options, 'root-intent', ['onboarding_only']);
166
148
  const productType = assertEnumOption(options, 'product-type', ['aipay', 'webpay', 'apppay'], { required: false });
149
+ process.env.ALIPAY_AIPAY_ROOT_INTENT = rootIntent;
167
150
  await startTelemetryRun(rootIntent, productType, options['input-id']);
168
151
  const { runOnboardingMessageCli } = await import('./onboarding_message_runner.mjs');
169
152
  const platformEntryArgs = ['platform-entry'];
170
- appendOptionalTerminalCountOptions(platformEntryArgs, options);
171
153
  await runOnboardingMessageCli(cliArgv(platformEntryArgs));
172
154
  const { isWindowsPlatform } = await import('./platform_compat.mjs');
173
155
  if (isWindowsPlatform()) return;