sap-ai-dev-toolkit 0.9.9915 → 0.9.9917

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/setup.mjs +27 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sap-ai-dev-toolkit",
3
- "version": "0.9.9915",
3
+ "version": "0.9.9917",
4
4
  "description": "SAP AI development toolkit for BAS, ABAP, RAP, CAP, HANA Cloud, Fiori, UI5, and MCP",
5
5
  "author": "Gurkan Yilmaz",
6
6
  "type": "module",
package/src/setup.mjs CHANGED
@@ -355,7 +355,8 @@ function cfFailure(error) {
355
355
  }
356
356
 
357
357
  // Verify one side's key online, then save it user-only. A key that cannot sign in is saved only when confirmed.
358
- async function acceptIntegrationKey(side, config, { env, input, output, verifyIntegration }) {
358
+ async function acceptIntegrationKey(side, config, context) {
359
+ const { env, input, output, verifyIntegration } = context;
359
360
  const choice = side.toLowerCase();
360
361
  const report = await printIntegrationVerification({ [choice]: config }, { env, output, verifyIntegration });
361
362
  if (!report[choice].signed_in && !await confirmPrompt('Save this key anyway? (y/n, Enter=no)', { input, output, defaultYes: false })) return { saved: false, report };
@@ -368,6 +369,7 @@ async function acceptIntegrationKey(side, config, { env, input, output, verifyIn
368
369
  // A broken environment value is reported by the status lines.
369
370
  }
370
371
  if (fromEnvironment) print(output, formatStatus(`${side} credentials from ${fromEnvironment.source} in this environment take precedence over the saved key.`, 'warning', output, 'Integration Suite'));
372
+ context.recordHealth?.(report);
371
373
  return { saved: true, report };
372
374
  }
373
375
 
@@ -457,23 +459,38 @@ async function useCloudFoundrySpace(target, context) {
457
459
  // when the environment has no key.
458
460
  async function configureIntegrationCredentials({ env, input, output, verifyIntegration, cloudFoundry = defaultIntegrationCloudFoundry }) {
459
461
  let status = integrationSuiteCredentialStatus(env);
460
- if (status.found) await printIntegrationVerification(status.config, { env, output, verifyIntegration });
462
+ // Latest verification per side ({ cpi, apim }); updated whenever a key is checked or saved.
463
+ const health = {};
464
+ const recordHealth = report => {
465
+ for (const side of ['cpi', 'apim']) if (report?.[side]?.configured) health[side] = report[side];
466
+ };
467
+ if (status.found) recordHealth(await printIntegrationVerification(status.config, { env, output, verifyIntegration }));
461
468
  let target = { available: false };
462
469
  try {
463
470
  target = await cloudFoundryStep('Checking the Cloud Foundry login', () => cloudFoundry.getTarget({ env }), { env, output });
464
471
  } catch {
465
472
  // No CF CLI or no login: the CF option is simply not offered.
466
473
  }
467
- const context = { env, input, output, verifyIntegration, cloudFoundry };
474
+ const context = { env, input, output, verifyIntegration, cloudFoundry, recordHealth };
468
475
  for (;;) {
469
476
  print(output, '');
470
477
  const space = target.available ? [target.orgName, target.spaceName].filter(Boolean).join(' / ') || target.spaceGuid : '';
478
+ // Sides whose key signs in but lacks roles: a new key cannot fix that, a dedicated instance can.
479
+ const roleless = ['CPI', 'APIM'].filter(side => lacksRoles(health[side.toLowerCase()]));
480
+ const healthy = status.found && !roleless.length && Object.values(health).every(side => side.ok);
481
+ const fixes = target.available
482
+ ? roleless.map(side => ({
483
+ name: `Create ${INTEGRATION_CF_SERVICES[side].dedicatedInstance} in ${space} with the roles the ${side} tools need, then use it`,
484
+ value: `create-${side.toLowerCase()}`
485
+ }))
486
+ : [];
471
487
  const choice = await selectPrompt({
472
488
  message: colorText('🔑 Integration Suite credentials', 'cyan', output),
473
- defaultValue: status.found ? 'done' : (target.available ? 'cf' : 'cpi'),
489
+ defaultValue: healthy ? 'done' : (fixes[0]?.value ?? (target.available ? 'cf' : (status.found ? 'done' : 'cpi'))),
474
490
  choices: [
475
- { name: status.found ? 'Continue' : 'Continue without credentials (the server will not start until a key is configured)', value: 'done', shortcut: 'c' },
476
- ...(target.available ? [{ name: `Use the logged-in Cloud Foundry space (${space}): find the instances, create or reuse service keys, verify`, value: 'cf', shortcut: 'f' }] : []),
491
+ ...fixes,
492
+ { name: !status.found ? 'Continue without credentials (the server will not start until a key is configured)' : (healthy ? 'Continue' : 'Continue with the current key (tools in the ✗ areas will fail with HTTP 403)'), value: 'done', shortcut: 'c' },
493
+ ...(target.available ? [{ name: `Use the logged-in CF space (${space}): find instances, reuse or create keys`, value: 'cf', shortcut: 'f' }] : []),
477
494
  { name: 'Paste a Cloud Integration (CPI) service key — Process Integration Runtime, plan api', value: 'cpi', shortcut: 'p' },
478
495
  { name: 'Paste an API Management service key — API Management, plan apiportal-apiaccess', value: 'apim', shortcut: 'a' },
479
496
  ...(status.found ? [{ name: 'Check the configured credentials online again', value: 'verify', shortcut: 'v' }] : [])
@@ -481,10 +498,12 @@ async function configureIntegrationCredentials({ env, input, output, verifyInteg
481
498
  }, { input, output });
482
499
  if (choice === 'done') return status;
483
500
  if (choice === 'verify') {
484
- await printIntegrationVerification(status.config, { env, output, verifyIntegration });
501
+ recordHealth(await printIntegrationVerification(status.config, { env, output, verifyIntegration }));
485
502
  continue;
486
503
  }
487
- if (choice === 'cf') {
504
+ if (choice.startsWith('create-')) {
505
+ await createDedicatedInstance(choice.slice('create-'.length).toUpperCase(), context);
506
+ } else if (choice === 'cf') {
488
507
  await useCloudFoundrySpace(target, context);
489
508
  } else {
490
509
  const side = choice.toUpperCase();