sap-ai-dev-toolkit 0.9.9918 → 0.9.9921
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.
- package/.github/agents/sap-integration-architect.agent.md +32 -18
- package/.github/agents/sap-integration-developer.agent.md +28 -12
- package/.github/skills/integration-suite-delivery/SKILL.md +94 -37
- package/.github/skills/sap-integration-architect/SKILL.md +49 -25
- package/README.md +72 -15
- package/dist/checksums.txt +5 -5
- package/dist/vsp-darwin-arm64 +0 -0
- package/dist/vsp-darwin-x64 +0 -0
- package/dist/vsp-linux-arm64 +0 -0
- package/dist/vsp-linux-x64 +0 -0
- package/dist/vsp-win32-x64.exe +0 -0
- package/inventory.md +97 -57
- package/package.json +1 -1
- package/scripts/check-live-integration.mjs +151 -0
- package/scripts/install-local.mjs +11 -4
- package/scripts/run-live-integration.mjs +55 -63
- package/src/apim-bundle.mjs +416 -13
- package/src/apim-cleanup-tools.mjs +145 -0
- package/src/apim-content.mjs +157 -0
- package/src/apim-delete-tools.mjs +139 -0
- package/src/apim-documentation.mjs +162 -0
- package/src/apim-inspection.mjs +141 -0
- package/src/apim-monitoring.mjs +140 -0
- package/src/apim-policy-steps.mjs +31 -0
- package/src/apim-runtime.mjs +262 -0
- package/src/apim-tools.mjs +202 -109
- package/src/cf-integration.mjs +46 -7
- package/src/cpi-async-monitoring.mjs +177 -0
- package/src/cpi-content.mjs +92 -0
- package/src/cpi-monitoring.mjs +225 -0
- package/src/cpi-operational.mjs +129 -0
- package/src/cpi-release.mjs +273 -0
- package/src/cpi-security.mjs +48 -0
- package/src/cpi-tools.mjs +184 -319
- package/src/devhub-retirement.mjs +345 -0
- package/src/devhub-tools.mjs +177 -0
- package/src/edge-monitoring.mjs +101 -0
- package/src/iflow-bundle.mjs +295 -7
- package/src/integration-alerts.mjs +248 -0
- package/src/integration-analysis.mjs +488 -0
- package/src/integration-capabilities.mjs +181 -0
- package/src/integration-collections.mjs +203 -0
- package/src/integration-common.mjs +140 -35
- package/src/integration-config.mjs +20 -14
- package/src/integration-credentials.mjs +16 -10
- package/src/integration-http.mjs +255 -67
- package/src/integration-messaging.mjs +118 -0
- package/src/integration-optional-config.mjs +122 -0
- package/src/integration-optional-tools.mjs +153 -0
- package/src/integration-release-store.mjs +410 -0
- package/src/integration-server.mjs +34 -16
- package/src/integration-state.mjs +31 -0
- package/src/integration-tools.mjs +44 -8
- package/src/integration-transport.mjs +184 -0
- package/src/integration-validation.mjs +26 -0
- package/src/integration-verify.mjs +143 -62
- package/src/open-connectors-tools.mjs +143 -0
- package/src/setup.mjs +86 -52
- package/tools.md +193 -26
package/src/setup.mjs
CHANGED
|
@@ -12,6 +12,7 @@ import { parseIntegrationServiceKey, resolveIntegrationConfig, resolveIntegratio
|
|
|
12
12
|
import { readStoredIntegrationKeys, storeIntegrationKey } from './integration-credentials.mjs';
|
|
13
13
|
import { INTEGRATION_CF_SERVICES, createIntegrationInstance, findIntegrationInstances, integrationServiceKey } from './cf-integration.mjs';
|
|
14
14
|
import { createIntegrationClients, verificationLines, verifyIntegrationClients } from './integration-verify.mjs';
|
|
15
|
+
import { OPTIONAL_COMPONENT_KEYS, OPTIONAL_INPUTS, parseOptionalConfig, resolveOptionalConfig } from './integration-optional-config.mjs';
|
|
15
16
|
import { discoverCertificateDnsNames } from './tls-adt-proxy.mjs';
|
|
16
17
|
import { findRemoteControllableSsoBrowsers } from './sso-browsers.mjs';
|
|
17
18
|
|
|
@@ -285,16 +286,26 @@ async function confirmPrompt(message, { input = stdin, output = stdout, defaultY
|
|
|
285
286
|
return !/^n(?:o)?$/i.test(trimmed);
|
|
286
287
|
}
|
|
287
288
|
|
|
288
|
-
//
|
|
289
|
-
// (CPI, APIM) resolve from this environment or the stored-key file, and from which source.
|
|
289
|
+
// Report configured component identities and source evidence without credential values.
|
|
290
290
|
export function integrationSuiteCredentialStatus(env = process.env) {
|
|
291
291
|
try {
|
|
292
292
|
const config = resolveIntegrationConfig(env, { stored: readStoredIntegrationKeys(env) });
|
|
293
|
-
const
|
|
294
|
-
|
|
293
|
+
const keys = ['cpi', 'apim', 'devhub', ...OPTIONAL_COMPONENT_KEYS];
|
|
294
|
+
const side = (key, value) => {
|
|
295
|
+
const host = value.url ? new URL(value.url).host : key === 'aem' ? 'api.solace.cloud' : 'unknown';
|
|
296
|
+
return `configured from ${value.source}${value.bindingName ? ` (${value.bindingName})` : ''}, host ${host}`;
|
|
297
|
+
};
|
|
298
|
+
const configured = keys.filter(key => config[key]);
|
|
299
|
+
const errors = (config.optional_config_errors ?? []).map(error => `${error.component.toUpperCase()}: invalid optional configuration (${error.source}); adapter disabled`);
|
|
300
|
+
if (!configured.length && !errors.length) return { found: false, config, lines: ['No Integration Suite credentials configured yet.'] };
|
|
301
|
+
return { found: configured.length > 0, config, lines: [
|
|
302
|
+
...(config.environment ? [`Environment label: ${config.environment}`] : []),
|
|
303
|
+
...configured.map(key => `${key.toUpperCase()}: ${side(key, config[key])}`),
|
|
304
|
+
...errors
|
|
305
|
+
] };
|
|
295
306
|
} catch (error) {
|
|
296
307
|
const message = String(error?.message || '');
|
|
297
|
-
if (/^Configure CPI/.test(message)) return { found: false, config: null, lines: ['No
|
|
308
|
+
if (/^Configure CPI/.test(message)) return { found: false, config: null, lines: ['No Integration Suite credentials configured yet.'] };
|
|
298
309
|
return { found: false, config: null, lines: [`Credentials found but not usable: ${message}`] };
|
|
299
310
|
}
|
|
300
311
|
}
|
|
@@ -315,28 +326,32 @@ function pastedServiceKey(side, value, env) {
|
|
|
315
326
|
throw new Error('Paste the service key JSON (starting with {) or the path to a readable .json file.');
|
|
316
327
|
}
|
|
317
328
|
}
|
|
318
|
-
return
|
|
329
|
+
return OPTIONAL_COMPONENT_KEYS.includes(side.toLowerCase())
|
|
330
|
+
? parseOptionalConfig(side.toLowerCase(), text, { source: 'setup', allowRawCtms: true })
|
|
331
|
+
: parseIntegrationServiceKey(side, text);
|
|
319
332
|
}
|
|
320
333
|
|
|
321
334
|
async function printIntegrationVerification(config, { env, output, verifyIntegration }) {
|
|
322
|
-
const stop = startProgress('
|
|
335
|
+
const stop = startProgress('Checking Integration Suite access', { output, env, label: 'Integration Suite' });
|
|
323
336
|
let report;
|
|
324
|
-
try {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
337
|
+
try { report = await verifyIntegration(config, { env }); }
|
|
338
|
+
finally { stop(); }
|
|
339
|
+
const host = side => config[side]?.url ? new URL(config[side].url).host : side === 'aem' && config.aem ? 'api.solace.cloud' : '';
|
|
340
|
+
for (const label of ['CPI', 'APIM', 'DEVHUB']) {
|
|
341
|
+
for (const line of verificationLines(label, host(label.toLowerCase()), report[label.toLowerCase()])) print(output, ` ${line}`);
|
|
328
342
|
}
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
if (report.ok) {
|
|
333
|
-
print(output, formatStatus(`Integration Suite credentials verified${missing ? `; ${missing} area${missing === 1 ? ' needs' : 's need'} an extra role on the service key` : ''}.`, missing ? 'warning' : 'success', output, 'Integration Suite'));
|
|
334
|
-
} else {
|
|
335
|
-
const signedIn = [report.cpi, report.apim].some(side => side?.signed_in);
|
|
336
|
-
print(output, formatStatus(signedIn
|
|
337
|
-
? 'The key signs in but lacks the roles for the core area; use an instance with the needed roles (the Cloud Foundry option can create one) or replace the key.'
|
|
338
|
-
: 'Integration Suite sign-in failed; fix or replace the service key before using the tools.', 'warning', output, 'Integration Suite'));
|
|
343
|
+
for (const side of OPTIONAL_COMPONENT_KEYS) {
|
|
344
|
+
const access = report[side];
|
|
345
|
+
if (access?.configured) print(output, ` ${side.toUpperCase()} ${host(side)}: ${access.result ?? access.checks?.[0]?.result ?? 'untested'} (optional access; activation/entitlement unknown)`);
|
|
339
346
|
}
|
|
347
|
+
const core = [report.cpi, report.apim, report.devhub].filter(side => side?.configured);
|
|
348
|
+
const denied = core.flatMap(side => side.checks ?? []).filter(check => check.result === 'forbidden').length;
|
|
349
|
+
const message = !core.length ? 'No core service configured yet.'
|
|
350
|
+
: report.ok ? (denied ? `Connected; ${denied} area(s) were access-denied — the key may lack roles.` : 'Connection verified.')
|
|
351
|
+
: 'Sign-in or core read failed; see the lines above.';
|
|
352
|
+
print(output, formatStatus(message, report.ok && !denied ? 'success' : 'warning', output, 'Integration Suite'));
|
|
353
|
+
const optional = OPTIONAL_COMPONENT_KEYS.filter(side => report[side]?.configured);
|
|
354
|
+
if (optional.length) print(output, ` Optional services: ${report.optional_completeness?.complete === true ? 'all verified' : 'not fully verified'} (separate from core access).`);
|
|
340
355
|
return report;
|
|
341
356
|
}
|
|
342
357
|
|
|
@@ -347,7 +362,7 @@ const defaultIntegrationCloudFoundry = Object.freeze({
|
|
|
347
362
|
createInstance: createIntegrationInstance
|
|
348
363
|
});
|
|
349
364
|
|
|
350
|
-
const SIDE_LABELS = Object.freeze({ CPI: 'Cloud Integration (CPI)', APIM: 'API Management' });
|
|
365
|
+
const SIDE_LABELS = Object.freeze({ CPI: 'Cloud Integration (CPI)', APIM: 'API Management', DEVHUB: 'Developer Hub' });
|
|
351
366
|
|
|
352
367
|
function cfFailure(error) {
|
|
353
368
|
const detail = String(error?.stderr || error?.message || '').replace(/\s+/g, ' ').trim();
|
|
@@ -359,12 +374,14 @@ async function acceptIntegrationKey(side, config, context) {
|
|
|
359
374
|
const { env, input, output, verifyIntegration } = context;
|
|
360
375
|
const choice = side.toLowerCase();
|
|
361
376
|
const report = await printIntegrationVerification({ [choice]: config }, { env, output, verifyIntegration });
|
|
362
|
-
if (!report[choice]
|
|
377
|
+
if (!report[choice]?.signed_in && !await confirmPrompt('Save these existing credentials despite unverified access? (y/n, Enter=no)', { input, output, defaultYes: false })) return { saved: false, report };
|
|
363
378
|
const path = await storeIntegrationKey(side, config, env);
|
|
364
379
|
print(output, formatStatus(`Saved the ${side} key to ${path} (readable only by you; not written to mcp.json).`, 'success', output, 'Integration Suite'));
|
|
365
380
|
let fromEnvironment = null;
|
|
366
381
|
try {
|
|
367
|
-
fromEnvironment =
|
|
382
|
+
fromEnvironment = OPTIONAL_COMPONENT_KEYS.includes(choice)
|
|
383
|
+
? resolveOptionalConfig(env, {})[choice]
|
|
384
|
+
: resolveIntegrationSideFromEnvironment(env, side);
|
|
368
385
|
} catch {
|
|
369
386
|
// A broken environment value is reported by the status lines.
|
|
370
387
|
}
|
|
@@ -374,7 +391,7 @@ async function acceptIntegrationKey(side, config, context) {
|
|
|
374
391
|
}
|
|
375
392
|
|
|
376
393
|
function lacksRoles(side) {
|
|
377
|
-
return Boolean(side?.signed_in) && (
|
|
394
|
+
return Boolean(side?.signed_in) && (side.checks ?? []).some(check => check.result === 'forbidden');
|
|
378
395
|
}
|
|
379
396
|
|
|
380
397
|
async function cloudFoundryStep(message, operation, { env, output }) {
|
|
@@ -418,13 +435,13 @@ async function useCloudFoundrySpace(target, context) {
|
|
|
418
435
|
print(output, formatStatus(`Could not list service instances: ${cfFailure(error)}`, 'warning', output, 'Cloud Foundry'));
|
|
419
436
|
return;
|
|
420
437
|
}
|
|
421
|
-
for (const side of ['CPI', 'APIM']) {
|
|
438
|
+
for (const side of ['CPI', 'APIM', 'DEVHUB']) {
|
|
422
439
|
const service = INTEGRATION_CF_SERVICES[side];
|
|
423
440
|
const candidates = instances[side] || [];
|
|
424
441
|
print(output, '');
|
|
425
442
|
if (!candidates.length) {
|
|
426
443
|
print(output, formatStatus(`No ${service.label} instance with plan ${service.plan} (${service.offering}) in this space.`, 'info', output, SIDE_LABELS[side]));
|
|
427
|
-
if (await confirmPrompt(`Create instance ${service.dedicatedInstance} (${service.offering}, plan ${service.plan}) with the roles the tools need? (y/n, Enter=no)`, { input, output, defaultYes: false })) {
|
|
444
|
+
if (await confirmPrompt(`Create instance ${service.dedicatedInstance} (${service.offering}, plan ${service.plan}) with ${service.parameters.role || 'the roles the tools need'}, then create or reuse its sap-ai-dev-integration service key? (y/n, Enter=no)`, { input, output, defaultYes: false })) {
|
|
428
445
|
await createDedicatedInstance(side, context);
|
|
429
446
|
}
|
|
430
447
|
continue;
|
|
@@ -436,6 +453,7 @@ async function useCloudFoundrySpace(target, context) {
|
|
|
436
453
|
choices: [...candidates.map(candidate => ({ name: candidate.name, value: candidate.name })), { name: 'Skip', value: '', shortcut: 's' }]
|
|
437
454
|
}, { input, output });
|
|
438
455
|
if (!instanceName) continue;
|
|
456
|
+
if (side === 'DEVHUB' && !await confirmPrompt(`Use ${instanceName} and create or reuse its sap-ai-dev-integration service key? (y/n, Enter=no)`, { input, output, defaultYes: false })) continue;
|
|
439
457
|
let result;
|
|
440
458
|
try {
|
|
441
459
|
result = await useCloudFoundryInstance(side, instanceName, context);
|
|
@@ -454,15 +472,35 @@ async function useCloudFoundrySpace(target, context) {
|
|
|
454
472
|
}
|
|
455
473
|
}
|
|
456
474
|
|
|
457
|
-
//
|
|
475
|
+
// The paste flow asks which service the key belongs to; APIM and Developer Hub keys share one OAuth shape,
|
|
476
|
+
// so the type is never guessed from the key itself.
|
|
477
|
+
async function chooseKeyType({ input, output, status }) {
|
|
478
|
+
return await selectPrompt({
|
|
479
|
+
message: colorText('Which service is this key for?', 'cyan', output),
|
|
480
|
+
defaultValue: ['CPI', 'APIM', 'DEVHUB'].find(side => !status.config?.[side.toLowerCase()]) ?? 'CPI',
|
|
481
|
+
choices: [
|
|
482
|
+
{ value: 'CPI', shortcut: 'i', name: 'Cloud Integration (CPI) — Process Integration Runtime, plan api' },
|
|
483
|
+
{ value: 'APIM', shortcut: 'a', name: 'API Management — plan apiportal-apiaccess' },
|
|
484
|
+
{ value: 'DEVHUB', shortcut: 'd', name: 'Developer Hub — plan devportal-apiaccess (AuthGroup.API.Admin)' },
|
|
485
|
+
...OPTIONAL_COMPONENT_KEYS.map(side => ({
|
|
486
|
+
value: side.toUpperCase(),
|
|
487
|
+
shortcut: { ctms: 't', ans: 'n', event_mesh: 'e', aem: 'm', aem_semp: 's', open_connectors: 'o', edge: 'g' }[side],
|
|
488
|
+
name: `Optional: ${OPTIONAL_INPUTS[side].label} (${OPTIONAL_INPUTS[side].variable}, existing credentials only)`
|
|
489
|
+
})),
|
|
490
|
+
{ value: '', shortcut: 'b', name: 'Back' }
|
|
491
|
+
]
|
|
492
|
+
}, { input, output });
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Verify the detected credentials online and let the user take CPI/APIM/DEVHUB keys from the logged-in CF space or
|
|
458
496
|
// paste them. A key is checked online first and saved to the user-only store (never to mcp.json); it is used
|
|
459
497
|
// when the environment has no key.
|
|
460
498
|
async function configureIntegrationCredentials({ env, input, output, verifyIntegration, cloudFoundry = defaultIntegrationCloudFoundry }) {
|
|
461
499
|
let status = integrationSuiteCredentialStatus(env);
|
|
462
|
-
// Latest verification per side ({ cpi, apim }); updated whenever a key is checked or saved.
|
|
500
|
+
// Latest verification per side ({ cpi, apim, devhub }); updated whenever a key is checked or saved.
|
|
463
501
|
const health = {};
|
|
464
502
|
const recordHealth = report => {
|
|
465
|
-
for (const side of ['cpi', 'apim']) if (report?.[side]?.configured) health[side] = report[side];
|
|
503
|
+
for (const side of ['cpi', 'apim', 'devhub', ...OPTIONAL_COMPONENT_KEYS]) if (report?.[side]?.configured) health[side] = report[side];
|
|
466
504
|
};
|
|
467
505
|
if (status.found) recordHealth(await printIntegrationVerification(status.config, { env, output, verifyIntegration }));
|
|
468
506
|
let target = { available: false };
|
|
@@ -475,9 +513,9 @@ async function configureIntegrationCredentials({ env, input, output, verifyInteg
|
|
|
475
513
|
for (;;) {
|
|
476
514
|
print(output, '');
|
|
477
515
|
const space = target.available ? [target.orgName, target.spaceName].filter(Boolean).join(' / ') || target.spaceGuid : '';
|
|
478
|
-
//
|
|
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);
|
|
516
|
+
// Access denial is observed; its role/entitlement cause is not established. Optional setup never provisions.
|
|
517
|
+
const roleless = ['CPI', 'APIM', 'DEVHUB'].filter(side => lacksRoles(health[side.toLowerCase()]));
|
|
518
|
+
const healthy = status.found && !roleless.length && Object.values(health).length > 0 && Object.values(health).every(side => side.ok);
|
|
481
519
|
const fixes = target.available
|
|
482
520
|
? roleless.map(side => ({
|
|
483
521
|
name: `Create ${INTEGRATION_CF_SERVICES[side].dedicatedInstance} in ${space} with the roles the ${side} tools need, then use it`,
|
|
@@ -486,14 +524,13 @@ async function configureIntegrationCredentials({ env, input, output, verifyInteg
|
|
|
486
524
|
: [];
|
|
487
525
|
const choice = await selectPrompt({
|
|
488
526
|
message: colorText('🔑 Integration Suite credentials', 'cyan', output),
|
|
489
|
-
defaultValue: healthy ? 'done' : (fixes[0]?.value ?? (target.available ? 'cf' : (status.found ? 'done' : '
|
|
527
|
+
defaultValue: healthy ? 'done' : (fixes[0]?.value ?? (target.available ? 'cf' : (status.found ? 'done' : 'paste'))),
|
|
490
528
|
choices: [
|
|
491
529
|
...fixes,
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
{
|
|
496
|
-
...(status.found ? [{ name: 'Check the configured credentials online again', value: 'verify', shortcut: 'v' }] : [])
|
|
530
|
+
...(target.available ? [{ value: 'cf', shortcut: 'f', name: `Get keys from Cloud Foundry (${space})` }] : []),
|
|
531
|
+
{ value: 'paste', shortcut: 'p', name: 'Paste a service key (JSON or path to a .json file)' },
|
|
532
|
+
...(status.found ? [{ value: 'verify', shortcut: 'v', name: 'Check credentials again' }] : []),
|
|
533
|
+
{ value: 'done', shortcut: 'c', name: status.found ? (healthy ? 'Done' : 'Done (some checks did not pass)') : 'Skip for now (run sap-ai-dev --setup-integration later)' }
|
|
497
534
|
]
|
|
498
535
|
}, { input, output });
|
|
499
536
|
if (choice === 'done') return status;
|
|
@@ -505,10 +542,11 @@ async function configureIntegrationCredentials({ env, input, output, verifyInteg
|
|
|
505
542
|
await createDedicatedInstance(choice.slice('create-'.length).toUpperCase(), context);
|
|
506
543
|
} else if (choice === 'cf') {
|
|
507
544
|
await useCloudFoundrySpace(target, context);
|
|
508
|
-
} else {
|
|
509
|
-
const side =
|
|
545
|
+
} else if (choice === 'paste') {
|
|
546
|
+
const side = await chooseKeyType({ input, output, status });
|
|
547
|
+
if (!side) continue; // Back: show the menu again without refreshing status.
|
|
510
548
|
const pasted = await textPrompt({
|
|
511
|
-
message: `Paste
|
|
549
|
+
message: `Paste existing ${side} credential JSON or the path to its .json file`,
|
|
512
550
|
secret: true,
|
|
513
551
|
validate: value => {
|
|
514
552
|
try {
|
|
@@ -528,16 +566,12 @@ async function configureIntegrationCredentials({ env, input, output, verifyInteg
|
|
|
528
566
|
|
|
529
567
|
async function promptIntegrationSuite({ env, alreadyConfigured, input, output, verifyIntegration = defaultVerifyIntegration, integrationCloudFoundry, ask = true }) {
|
|
530
568
|
print(output, '');
|
|
531
|
-
print(output, formatStatus('SAP Integration Suite tools
|
|
532
|
-
print(output, '
|
|
533
|
-
print(output, ' Every write needs an exact confirmation string; there are no delete tools and secrets are write-only. Agents: SAP Integration Architect, SAP Integration Developer.');
|
|
569
|
+
print(output, formatStatus('SAP Integration Suite tools', 'step', output, 'Integration Suite'));
|
|
570
|
+
print(output, ' Connects your AI agent to Cloud Integration (CPI), API Management and Developer Hub. Keys are checked read-only and stored only for you, never in mcp.json.');
|
|
534
571
|
if (alreadyConfigured) print(output, ' Already configured in this MCP config; keeping it and refreshing its version.');
|
|
535
572
|
const status = integrationSuiteCredentialStatus(env);
|
|
536
573
|
for (const line of status.lines) print(output, ` ${line}`);
|
|
537
|
-
if (!status.found)
|
|
538
|
-
print(output, ' The server needs CPI_SERVICE_KEY and/or APIM_SERVICE_KEY (or CPI_*/APIM_* variables, VCAP_SERVICES) in the MCP host environment,');
|
|
539
|
-
print(output, ' or a service key pasted in the next step (verified online, then saved for your user only). Credentials are never written to mcp.json.');
|
|
540
|
-
}
|
|
574
|
+
if (!status.found) print(output, ' No credentials found yet. Next you can get them from your Cloud Foundry space or paste a service key.');
|
|
541
575
|
if (ask && !alreadyConfigured) {
|
|
542
576
|
print(output, '');
|
|
543
577
|
const defaultYes = status.found;
|
|
@@ -1062,7 +1096,7 @@ export async function runSetup({
|
|
|
1062
1096
|
defaultValue: 'manual',
|
|
1063
1097
|
choices: [
|
|
1064
1098
|
{ name: '✍️ Start manual entry wizard (system name, ADT URL, client, login)', value: 'manual', shortcut: 'm' },
|
|
1065
|
-
{ name: '🔌 Set up only the SAP Integration Suite tools (paste and verify CPI / API Management service keys)', value: 'integration', shortcut: 'i' },
|
|
1099
|
+
{ name: '🔌 Set up only the SAP Integration Suite tools (paste and verify CPI / API Management / Developer Hub service keys)', value: 'integration', shortcut: 'i' },
|
|
1066
1100
|
{ name: 'Exit setup without changes', value: 'exit', shortcut: 'e' }
|
|
1067
1101
|
]
|
|
1068
1102
|
}, { input, output });
|
|
@@ -1087,9 +1121,9 @@ export async function runSetup({
|
|
|
1087
1121
|
sapDevelopmentServers = ['integration-suite'];
|
|
1088
1122
|
} else if (includeSapDevelopmentToolsPrompt) {
|
|
1089
1123
|
print(output, '');
|
|
1090
|
-
print(output, formatStatus('Optionally add companion MCP servers for full-stack SAP development, HANA inspection, and Integration Suite (CPI/API Management).', 'step', output, 'Tools'));
|
|
1124
|
+
print(output, formatStatus('Optionally add companion MCP servers for full-stack SAP development, HANA inspection, and Integration Suite (CPI/API Management/Developer Hub).', 'step', output, 'Tools'));
|
|
1091
1125
|
print(output, ' HANA Cloud inspector is read-only and uses HANA_RO_* or VCAP_SERVICES already present in the MCP host environment; credentials are not written to mcp.json.');
|
|
1092
|
-
print(output, ' Integration Suite uses CPI/APIM service keys from the MCP host environment or keys you paste next (verified online, saved for your user only); credentials are not written to mcp.json.');
|
|
1126
|
+
print(output, ' Integration Suite uses CPI/APIM/DEVHUB service keys from the MCP host environment or keys you paste next (verified online, saved for your user only); credentials are not written to mcp.json.');
|
|
1093
1127
|
print(output, ' Leave empty to only configure ABAP/ADT destination servers.');
|
|
1094
1128
|
print(output, '');
|
|
1095
1129
|
sapDevelopmentServers = await checkboxPrompt({
|