arc-1 0.7.1 → 0.8.0
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/README.md +35 -3
- package/dist/adt/btp.d.ts +14 -3
- package/dist/adt/btp.d.ts.map +1 -1
- package/dist/adt/btp.js +18 -3
- package/dist/adt/btp.js.map +1 -1
- package/dist/adt/client.d.ts +33 -19
- package/dist/adt/client.d.ts.map +1 -1
- package/dist/adt/client.js +84 -72
- package/dist/adt/client.js.map +1 -1
- package/dist/adt/diagnostics.d.ts +5 -1
- package/dist/adt/diagnostics.d.ts.map +1 -1
- package/dist/adt/diagnostics.js +20 -3
- package/dist/adt/diagnostics.js.map +1 -1
- package/dist/adt/discovery.d.ts +19 -2
- package/dist/adt/discovery.d.ts.map +1 -1
- package/dist/adt/discovery.js +17 -3
- package/dist/adt/discovery.js.map +1 -1
- package/dist/adt/errors.d.ts +1 -1
- package/dist/adt/errors.d.ts.map +1 -1
- package/dist/adt/errors.js +46 -2
- package/dist/adt/errors.js.map +1 -1
- package/dist/adt/features.d.ts +49 -0
- package/dist/adt/features.d.ts.map +1 -1
- package/dist/adt/features.js +109 -21
- package/dist/adt/features.js.map +1 -1
- package/dist/adt/types.d.ts +4 -0
- package/dist/adt/types.d.ts.map +1 -1
- package/dist/adt/xml-parser.d.ts +13 -5
- package/dist/adt/xml-parser.d.ts.map +1 -1
- package/dist/adt/xml-parser.js +55 -22
- package/dist/adt/xml-parser.js.map +1 -1
- package/dist/cache/cache.d.ts +11 -6
- package/dist/cache/cache.d.ts.map +1 -1
- package/dist/cache/cache.js +4 -4
- package/dist/cache/cache.js.map +1 -1
- package/dist/cache/caching-layer.d.ts +16 -4
- package/dist/cache/caching-layer.d.ts.map +1 -1
- package/dist/cache/caching-layer.js +40 -13
- package/dist/cache/caching-layer.js.map +1 -1
- package/dist/cache/inactive-list-cache.d.ts +15 -0
- package/dist/cache/inactive-list-cache.d.ts.map +1 -0
- package/dist/cache/inactive-list-cache.js +37 -0
- package/dist/cache/inactive-list-cache.js.map +1 -0
- package/dist/cache/memory.d.ts +6 -3
- package/dist/cache/memory.d.ts.map +1 -1
- package/dist/cache/memory.js +14 -6
- package/dist/cache/memory.js.map +1 -1
- package/dist/cache/sqlite.d.ts +7 -3
- package/dist/cache/sqlite.d.ts.map +1 -1
- package/dist/cache/sqlite.js +32 -9
- package/dist/cache/sqlite.js.map +1 -1
- package/dist/cache/warmup.js +7 -3
- package/dist/cache/warmup.js.map +1 -1
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/context/compressor.d.ts.map +1 -1
- package/dist/context/compressor.js +15 -12
- package/dist/context/compressor.js.map +1 -1
- package/dist/handlers/hyperfocused.d.ts.map +1 -1
- package/dist/handlers/hyperfocused.js +7 -0
- package/dist/handlers/hyperfocused.js.map +1 -1
- package/dist/handlers/intent.d.ts.map +1 -1
- package/dist/handlers/intent.js +163 -89
- package/dist/handlers/intent.js.map +1 -1
- package/dist/handlers/schemas.d.ts +12 -0
- package/dist/handlers/schemas.d.ts.map +1 -1
- package/dist/handlers/schemas.js +4 -0
- package/dist/handlers/schemas.js.map +1 -1
- package/dist/handlers/tools.d.ts.map +1 -1
- package/dist/handlers/tools.js +13 -2
- package/dist/handlers/tools.js.map +1 -1
- package/dist/server/audit.d.ts +41 -1
- package/dist/server/audit.d.ts.map +1 -1
- package/dist/server/audit.js.map +1 -1
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/config.js +32 -0
- package/dist/server/config.js.map +1 -1
- package/dist/server/http.d.ts +25 -0
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +149 -7
- package/dist/server/http.js.map +1 -1
- package/dist/server/server.d.ts +1 -1
- package/dist/server/server.js +1 -1
- package/dist/server/stateless-client-store.d.ts +97 -0
- package/dist/server/stateless-client-store.d.ts.map +1 -0
- package/dist/server/stateless-client-store.js +334 -0
- package/dist/server/stateless-client-store.js.map +1 -0
- package/dist/server/types.d.ts +12 -0
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +2 -0
- package/dist/server/types.js.map +1 -1
- package/dist/server/xsuaa.d.ts +11 -43
- package/dist/server/xsuaa.d.ts.map +1 -1
- package/dist/server/xsuaa.js +12 -177
- package/dist/server/xsuaa.js.map +1 -1
- package/package.json +8 -2
package/dist/handlers/intent.js
CHANGED
|
@@ -224,7 +224,7 @@ function buildBaseErrorMessage(err, message, tool, args) {
|
|
|
224
224
|
// Append additional SAP messages (line numbers, secondary errors) if available
|
|
225
225
|
const enriched = enrichWithSapDetails(err, message);
|
|
226
226
|
const argType = String(args.type ?? '').toUpperCase();
|
|
227
|
-
const classification = classifySapDomainError(err.statusCode, err.responseBody);
|
|
227
|
+
const classification = classifySapDomainError(err.statusCode, err.responseBody, err.path);
|
|
228
228
|
if (classification) {
|
|
229
229
|
const transactionLine = classification.transaction ? `\nSAP Transaction: ${classification.transaction}` : '';
|
|
230
230
|
return `${enriched}\n\nHint: ${classification.hint}${transactionLine}`;
|
|
@@ -712,7 +712,7 @@ function getBehaviorPoolSaveFailureHint(err, args) {
|
|
|
712
712
|
}
|
|
713
713
|
function classifyError(err) {
|
|
714
714
|
if (err instanceof AdtApiError) {
|
|
715
|
-
const classification = classifySapDomainError(err.statusCode, err.responseBody);
|
|
715
|
+
const classification = classifySapDomainError(err.statusCode, err.responseBody, err.path);
|
|
716
716
|
return classification ? `AdtApiError:${classification.category}` : 'AdtApiError';
|
|
717
717
|
}
|
|
718
718
|
if (err instanceof AdtNetworkError)
|
|
@@ -834,7 +834,7 @@ export async function handleToolCall(client, config, toolName, args, authInfo, _
|
|
|
834
834
|
result = await handleSAPWrite(client, args, config, cachingLayer);
|
|
835
835
|
break;
|
|
836
836
|
case 'SAPActivate':
|
|
837
|
-
result = await handleSAPActivate(client, args);
|
|
837
|
+
result = await handleSAPActivate(client, args, cachingLayer);
|
|
838
838
|
break;
|
|
839
839
|
case 'SAPNavigate':
|
|
840
840
|
result = await handleSAPNavigate(client, args);
|
|
@@ -928,23 +928,92 @@ const BTP_HINTS = {
|
|
|
928
928
|
SOBJ: 'BOR business objects (SOBJ) are not available on BTP ABAP Environment. Use RAP behavior definitions (BDEF) instead.',
|
|
929
929
|
TRAN: 'Transaction codes (TRAN) are not available on BTP ABAP Environment. Use SAPSearch to find apps and services instead.',
|
|
930
930
|
};
|
|
931
|
+
const VERSIONED_SOURCE_READ_TYPES = new Set([
|
|
932
|
+
'PROG',
|
|
933
|
+
'CLAS',
|
|
934
|
+
'INTF',
|
|
935
|
+
'FUNC',
|
|
936
|
+
'INCL',
|
|
937
|
+
'DDLS',
|
|
938
|
+
'DCLS',
|
|
939
|
+
'BDEF',
|
|
940
|
+
'SRVD',
|
|
941
|
+
'DDLX',
|
|
942
|
+
'SRVB',
|
|
943
|
+
'SKTD',
|
|
944
|
+
'TABL',
|
|
945
|
+
'VIEW',
|
|
946
|
+
'STRU',
|
|
947
|
+
]);
|
|
948
|
+
function inactiveTypeMatches(readType, inactiveType) {
|
|
949
|
+
return (inactiveType.split('/')[0] ?? inactiveType).toUpperCase() === readType.toUpperCase();
|
|
950
|
+
}
|
|
951
|
+
async function resolveVersionAndDraftInfo(client, cachingLayer, type, name, requestedVersion) {
|
|
952
|
+
if (!VERSIONED_SOURCE_READ_TYPES.has(type)) {
|
|
953
|
+
return { effectiveVersion: requestedVersion === 'auto' ? 'active' : requestedVersion };
|
|
954
|
+
}
|
|
955
|
+
let draft;
|
|
956
|
+
if (cachingLayer || requestedVersion !== 'active') {
|
|
957
|
+
try {
|
|
958
|
+
const inactiveObjects = cachingLayer
|
|
959
|
+
? await cachingLayer.inactiveLists.getOrFetch(client)
|
|
960
|
+
: await client.getInactiveObjects();
|
|
961
|
+
const upperName = name.toUpperCase();
|
|
962
|
+
draft = inactiveObjects.find((object) => inactiveTypeMatches(type, object.type) && object.name.toUpperCase() === upperName);
|
|
963
|
+
}
|
|
964
|
+
catch (err) {
|
|
965
|
+
logger.debug('Inactive object list unavailable while resolving source version', {
|
|
966
|
+
type,
|
|
967
|
+
name,
|
|
968
|
+
requestedVersion,
|
|
969
|
+
error: err instanceof Error ? err.message : String(err),
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
if (requestedVersion === 'auto') {
|
|
974
|
+
return { effectiveVersion: draft ? 'inactive' : 'active', draft };
|
|
975
|
+
}
|
|
976
|
+
return { effectiveVersion: requestedVersion, draft };
|
|
977
|
+
}
|
|
978
|
+
function sourceVersionWarning(effectiveVersion, draft) {
|
|
979
|
+
if (effectiveVersion === 'active' && draft) {
|
|
980
|
+
const deletion = draft.deleted ? ' deletion' : '';
|
|
981
|
+
const transport = draft.transport ? ` (in transport ${draft.transport})` : '';
|
|
982
|
+
return `Note: You have an unactivated${deletion} draft of this object${transport}. The source below is the LAST ACTIVATED version. To work with your draft, activate it first via SAPActivate or re-run with version='inactive' to read it directly.`;
|
|
983
|
+
}
|
|
984
|
+
if (effectiveVersion === 'inactive' && !draft) {
|
|
985
|
+
return 'Note: No inactive draft exists for this object on the server. Returning the active version.';
|
|
986
|
+
}
|
|
987
|
+
return undefined;
|
|
988
|
+
}
|
|
931
989
|
async function handleSAPRead(client, args, cachingLayer) {
|
|
932
990
|
const type = normalizeObjectType(String(args.type ?? ''));
|
|
933
991
|
const name = String(args.name ?? '');
|
|
992
|
+
const requestedVersion = (args.version ?? 'active');
|
|
934
993
|
// BTP: return helpful error for unavailable types
|
|
935
994
|
if (isBtpSystem() && BTP_HINTS[type]) {
|
|
936
995
|
return errorResult(BTP_HINTS[type]);
|
|
937
996
|
}
|
|
997
|
+
if (args.force_refresh === true && cachingLayer && VERSIONED_SOURCE_READ_TYPES.has(type)) {
|
|
998
|
+
cachingLayer.inactiveLists.invalidate(client.username);
|
|
999
|
+
cachingLayer.invalidate(type, name, 'all');
|
|
1000
|
+
}
|
|
1001
|
+
const { effectiveVersion, draft } = await resolveVersionAndDraftInfo(client, cachingLayer, type, name, requestedVersion);
|
|
1002
|
+
const versionWarning = sourceVersionWarning(effectiveVersion, draft);
|
|
938
1003
|
// Helper: get source with cache support, returns cache hit status
|
|
939
|
-
const cachedGet = async (objType, objName, fetcher) => {
|
|
940
|
-
if (!cachingLayer)
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1004
|
+
const cachedGet = async (objType, objName, version, fetcher) => {
|
|
1005
|
+
if (!cachingLayer) {
|
|
1006
|
+
const result = await fetcher(undefined);
|
|
1007
|
+
return { source: result.source, cacheHit: false, revalidated: false };
|
|
1008
|
+
}
|
|
1009
|
+
const { source, hit, revalidated } = await cachingLayer.getSource(objType, objName, fetcher, { version });
|
|
1010
|
+
return { source, cacheHit: hit, revalidated };
|
|
944
1011
|
};
|
|
945
|
-
/** Prepend
|
|
946
|
-
const cachedTextResult = (source, cacheHit) => {
|
|
947
|
-
|
|
1012
|
+
/** Prepend draft-awareness notes and cache indicator when the server revalidated a cached source. */
|
|
1013
|
+
const cachedTextResult = (source, cacheHit, revalidated, warning) => {
|
|
1014
|
+
const note = warning ? `${warning}\n\n` : '';
|
|
1015
|
+
const indicator = cacheHit && revalidated ? '[cached:revalidated]\n' : '';
|
|
1016
|
+
return textResult(`${note}${indicator}${source}`);
|
|
948
1017
|
};
|
|
949
1018
|
// Structured format is only supported for CLAS type
|
|
950
1019
|
if (args.format === 'structured' && type !== 'CLAS') {
|
|
@@ -952,8 +1021,8 @@ async function handleSAPRead(client, args, cachingLayer) {
|
|
|
952
1021
|
}
|
|
953
1022
|
switch (type) {
|
|
954
1023
|
case 'PROG': {
|
|
955
|
-
const { source, cacheHit } = await cachedGet('PROG', name, () => client.getProgram(name));
|
|
956
|
-
return cachedTextResult(source, cacheHit);
|
|
1024
|
+
const { source, cacheHit, revalidated } = await cachedGet('PROG', name, effectiveVersion, (ifNoneMatch) => client.getProgram(name, { ifNoneMatch, version: effectiveVersion }));
|
|
1025
|
+
return cachedTextResult(source, cacheHit, revalidated, versionWarning);
|
|
957
1026
|
}
|
|
958
1027
|
case 'CLAS': {
|
|
959
1028
|
// Structured format: return JSON with metadata + decomposed source
|
|
@@ -964,7 +1033,7 @@ async function handleSAPRead(client, args, cachingLayer) {
|
|
|
964
1033
|
const methodParam = args.method;
|
|
965
1034
|
if (methodParam && !args.include) {
|
|
966
1035
|
// Method-level read — fetch full source then extract (no cache indicator for derived results)
|
|
967
|
-
const { source: fullSource } = await cachedGet('CLAS', name, () => client.getClass(name));
|
|
1036
|
+
const { source: fullSource } = await cachedGet('CLAS', name, effectiveVersion, (ifNoneMatch) => client.getClass(name, undefined, { ifNoneMatch, version: effectiveVersion }));
|
|
968
1037
|
const abaplintVer = cachedFeatures?.abapRelease
|
|
969
1038
|
? mapSapReleaseToAbaplintVersion(cachedFeatures.abapRelease)
|
|
970
1039
|
: undefined;
|
|
@@ -976,18 +1045,21 @@ async function handleSAPRead(client, args, cachingLayer) {
|
|
|
976
1045
|
if (!extracted.success) {
|
|
977
1046
|
return errorResult(extracted.error ?? `Method "${methodParam}" not found in ${name}.`);
|
|
978
1047
|
}
|
|
979
|
-
return
|
|
1048
|
+
return cachedTextResult(extracted.methodSource, false, false, versionWarning);
|
|
980
1049
|
}
|
|
981
1050
|
// Only cache the full merged source (no include param), not individual includes
|
|
982
1051
|
if (!args.include) {
|
|
983
|
-
const { source, cacheHit } = await cachedGet('CLAS', name, () => client.getClass(name));
|
|
984
|
-
return cachedTextResult(source, cacheHit);
|
|
1052
|
+
const { source, cacheHit, revalidated } = await cachedGet('CLAS', name, effectiveVersion, (ifNoneMatch) => client.getClass(name, undefined, { ifNoneMatch, version: effectiveVersion }));
|
|
1053
|
+
return cachedTextResult(source, cacheHit, revalidated, versionWarning);
|
|
985
1054
|
}
|
|
986
|
-
|
|
1055
|
+
const includeResult = await client.getClass(name, args.include, {
|
|
1056
|
+
version: effectiveVersion,
|
|
1057
|
+
});
|
|
1058
|
+
return cachedTextResult(includeResult.source, false, false, versionWarning);
|
|
987
1059
|
}
|
|
988
1060
|
case 'INTF': {
|
|
989
|
-
const { source, cacheHit } = await cachedGet('INTF', name, () => client.getInterface(name));
|
|
990
|
-
return cachedTextResult(source, cacheHit);
|
|
1061
|
+
const { source, cacheHit, revalidated } = await cachedGet('INTF', name, effectiveVersion, (ifNoneMatch) => client.getInterface(name, { ifNoneMatch, version: effectiveVersion }));
|
|
1062
|
+
return cachedTextResult(source, cacheHit, revalidated, versionWarning);
|
|
991
1063
|
}
|
|
992
1064
|
case 'FUNC': {
|
|
993
1065
|
let group = String(args.group ?? '');
|
|
@@ -1001,13 +1073,13 @@ async function handleSAPRead(client, args, cachingLayer) {
|
|
|
1001
1073
|
}
|
|
1002
1074
|
group = resolved;
|
|
1003
1075
|
}
|
|
1004
|
-
const { source, cacheHit } = await cachedGet('FUNC', name, () => client.getFunction(group, name));
|
|
1005
|
-
return cachedTextResult(source, cacheHit);
|
|
1076
|
+
const { source, cacheHit, revalidated } = await cachedGet('FUNC', name, effectiveVersion, (ifNoneMatch) => client.getFunction(group, name, { ifNoneMatch, version: effectiveVersion }));
|
|
1077
|
+
return cachedTextResult(source, cacheHit, revalidated, versionWarning);
|
|
1006
1078
|
}
|
|
1007
1079
|
case 'FUGR': {
|
|
1008
1080
|
const expand = Boolean(args.expand_includes);
|
|
1009
1081
|
if (expand) {
|
|
1010
|
-
const source = await client.getFunctionGroupSource(name);
|
|
1082
|
+
const { source } = await client.getFunctionGroupSource(name, { version: effectiveVersion });
|
|
1011
1083
|
// Match INCLUDE statements but skip ABAP comment lines (starting with *)
|
|
1012
1084
|
const includePattern = /^[^*\n]*\bINCLUDE\s+(\S+)\s*\./gim;
|
|
1013
1085
|
const parts = [`=== FUGR ${name} (main) ===\n${source}`];
|
|
@@ -1015,7 +1087,7 @@ async function handleSAPRead(client, args, cachingLayer) {
|
|
|
1015
1087
|
while ((m = includePattern.exec(source)) !== null) {
|
|
1016
1088
|
const inclName = m[1];
|
|
1017
1089
|
try {
|
|
1018
|
-
const inclSource = await client.getInclude(inclName);
|
|
1090
|
+
const { source: inclSource } = await client.getInclude(inclName, { version: effectiveVersion });
|
|
1019
1091
|
parts.push(`\n=== ${inclName} ===\n${inclSource}`);
|
|
1020
1092
|
}
|
|
1021
1093
|
catch {
|
|
@@ -1028,37 +1100,37 @@ async function handleSAPRead(client, args, cachingLayer) {
|
|
|
1028
1100
|
return textResult(JSON.stringify(fg, null, 2));
|
|
1029
1101
|
}
|
|
1030
1102
|
case 'INCL': {
|
|
1031
|
-
const { source, cacheHit } = await cachedGet('INCL', name, () => client.getInclude(name));
|
|
1032
|
-
return cachedTextResult(source, cacheHit);
|
|
1103
|
+
const { source, cacheHit, revalidated } = await cachedGet('INCL', name, effectiveVersion, (ifNoneMatch) => client.getInclude(name, { ifNoneMatch, version: effectiveVersion }));
|
|
1104
|
+
return cachedTextResult(source, cacheHit, revalidated, versionWarning);
|
|
1033
1105
|
}
|
|
1034
1106
|
case 'DDLS': {
|
|
1035
|
-
const { source: ddlSource, cacheHit } = await cachedGet('DDLS', name, () => client.getDdls(name));
|
|
1107
|
+
const { source: ddlSource, cacheHit, revalidated, } = await cachedGet('DDLS', name, effectiveVersion, (ifNoneMatch) => client.getDdls(name, { ifNoneMatch, version: effectiveVersion }));
|
|
1036
1108
|
if (ddlSource.trim() === '') {
|
|
1037
1109
|
return textResult(`DDLS ${name} exists in the object directory but has no source code stored. ` +
|
|
1038
1110
|
`The DDL source may need to be written via SAPWrite(action="create" or "update", type="DDLS", name="${name}", source="...").`);
|
|
1039
1111
|
}
|
|
1040
1112
|
if (args.include?.toLowerCase() === 'elements') {
|
|
1041
1113
|
// Elements extraction is derived from source — no cache indicator
|
|
1042
|
-
return
|
|
1114
|
+
return cachedTextResult(extractCdsElements(ddlSource, name), false, false, versionWarning);
|
|
1043
1115
|
}
|
|
1044
|
-
return cachedTextResult(ddlSource, cacheHit);
|
|
1116
|
+
return cachedTextResult(ddlSource, cacheHit, revalidated, versionWarning);
|
|
1045
1117
|
}
|
|
1046
1118
|
case 'DCLS': {
|
|
1047
|
-
const { source, cacheHit } = await cachedGet('DCLS', name, () => client.getDcl(name));
|
|
1048
|
-
return cachedTextResult(source, cacheHit);
|
|
1119
|
+
const { source, cacheHit, revalidated } = await cachedGet('DCLS', name, effectiveVersion, (ifNoneMatch) => client.getDcl(name, { ifNoneMatch, version: effectiveVersion }));
|
|
1120
|
+
return cachedTextResult(source, cacheHit, revalidated, versionWarning);
|
|
1049
1121
|
}
|
|
1050
1122
|
case 'BDEF': {
|
|
1051
|
-
const { source, cacheHit } = await cachedGet('BDEF', name, () => client.getBdef(name));
|
|
1052
|
-
return cachedTextResult(source, cacheHit);
|
|
1123
|
+
const { source, cacheHit, revalidated } = await cachedGet('BDEF', name, effectiveVersion, (ifNoneMatch) => client.getBdef(name, { ifNoneMatch, version: effectiveVersion }));
|
|
1124
|
+
return cachedTextResult(source, cacheHit, revalidated, versionWarning);
|
|
1053
1125
|
}
|
|
1054
1126
|
case 'SRVD': {
|
|
1055
|
-
const { source, cacheHit } = await cachedGet('SRVD', name, () => client.getSrvd(name));
|
|
1056
|
-
return cachedTextResult(source, cacheHit);
|
|
1127
|
+
const { source, cacheHit, revalidated } = await cachedGet('SRVD', name, effectiveVersion, (ifNoneMatch) => client.getSrvd(name, { ifNoneMatch, version: effectiveVersion }));
|
|
1128
|
+
return cachedTextResult(source, cacheHit, revalidated, versionWarning);
|
|
1057
1129
|
}
|
|
1058
1130
|
case 'DDLX': {
|
|
1059
1131
|
try {
|
|
1060
|
-
const { source, cacheHit } = await cachedGet('DDLX', name, () => client.getDdlx(name));
|
|
1061
|
-
return cachedTextResult(source, cacheHit);
|
|
1132
|
+
const { source, cacheHit, revalidated } = await cachedGet('DDLX', name, effectiveVersion, (ifNoneMatch) => client.getDdlx(name, { ifNoneMatch, version: effectiveVersion }));
|
|
1133
|
+
return cachedTextResult(source, cacheHit, revalidated, versionWarning);
|
|
1062
1134
|
}
|
|
1063
1135
|
catch (err) {
|
|
1064
1136
|
if (isNotFoundError(err)) {
|
|
@@ -1068,16 +1140,16 @@ async function handleSAPRead(client, args, cachingLayer) {
|
|
|
1068
1140
|
}
|
|
1069
1141
|
}
|
|
1070
1142
|
case 'SRVB': {
|
|
1071
|
-
const { source, cacheHit } = await cachedGet('SRVB', name, () => client.getSrvb(name));
|
|
1072
|
-
return cachedTextResult(source, cacheHit);
|
|
1143
|
+
const { source, cacheHit, revalidated } = await cachedGet('SRVB', name, effectiveVersion, (ifNoneMatch) => client.getSrvb(name, { ifNoneMatch, version: effectiveVersion }));
|
|
1144
|
+
return cachedTextResult(source, cacheHit, revalidated, versionWarning);
|
|
1073
1145
|
}
|
|
1074
1146
|
case 'SKTD': {
|
|
1075
1147
|
try {
|
|
1076
1148
|
// ADT returns a <sktd:docu> XML envelope with the Markdown body base64-encoded
|
|
1077
1149
|
// inside <sktd:text>. Cache the raw envelope (update flow re-uses it) and
|
|
1078
1150
|
// return the decoded Markdown to the LLM.
|
|
1079
|
-
const { source, cacheHit } = await cachedGet('SKTD', name, () => client.getKtd(name));
|
|
1080
|
-
return cachedTextResult(decodeKtdText(source), cacheHit);
|
|
1151
|
+
const { source, cacheHit, revalidated } = await cachedGet('SKTD', name, effectiveVersion, (ifNoneMatch) => client.getKtd(name, { ifNoneMatch, version: effectiveVersion }));
|
|
1152
|
+
return cachedTextResult(decodeKtdText(source), cacheHit, revalidated, versionWarning);
|
|
1081
1153
|
}
|
|
1082
1154
|
catch (err) {
|
|
1083
1155
|
if (isNotFoundError(err)) {
|
|
@@ -1087,16 +1159,16 @@ async function handleSAPRead(client, args, cachingLayer) {
|
|
|
1087
1159
|
}
|
|
1088
1160
|
}
|
|
1089
1161
|
case 'TABL': {
|
|
1090
|
-
const { source, cacheHit } = await cachedGet('TABL', name, () => client.getTable(name));
|
|
1091
|
-
return cachedTextResult(source, cacheHit);
|
|
1162
|
+
const { source, cacheHit, revalidated } = await cachedGet('TABL', name, effectiveVersion, (ifNoneMatch) => client.getTable(name, { ifNoneMatch, version: effectiveVersion }));
|
|
1163
|
+
return cachedTextResult(source, cacheHit, revalidated, versionWarning);
|
|
1092
1164
|
}
|
|
1093
1165
|
case 'VIEW': {
|
|
1094
|
-
const { source, cacheHit } = await cachedGet('VIEW', name, () => client.getView(name));
|
|
1095
|
-
return cachedTextResult(source, cacheHit);
|
|
1166
|
+
const { source, cacheHit, revalidated } = await cachedGet('VIEW', name, effectiveVersion, (ifNoneMatch) => client.getView(name, { ifNoneMatch, version: effectiveVersion }));
|
|
1167
|
+
return cachedTextResult(source, cacheHit, revalidated, versionWarning);
|
|
1096
1168
|
}
|
|
1097
1169
|
case 'STRU': {
|
|
1098
|
-
const { source, cacheHit } = await cachedGet('STRU', name, () => client.getStructure(name));
|
|
1099
|
-
return cachedTextResult(source, cacheHit);
|
|
1170
|
+
const { source, cacheHit, revalidated } = await cachedGet('STRU', name, effectiveVersion, (ifNoneMatch) => client.getStructure(name, { ifNoneMatch, version: effectiveVersion }));
|
|
1171
|
+
return cachedTextResult(source, cacheHit, revalidated, versionWarning);
|
|
1100
1172
|
}
|
|
1101
1173
|
case 'DOMA': {
|
|
1102
1174
|
const domain = await client.getDomain(name);
|
|
@@ -1208,7 +1280,7 @@ async function handleSAPRead(client, args, cachingLayer) {
|
|
|
1208
1280
|
if (!prog) {
|
|
1209
1281
|
return errorResult(`BOR method "${method}" on "${name}" has no program assigned.`);
|
|
1210
1282
|
}
|
|
1211
|
-
const source = await client.getProgram(prog);
|
|
1283
|
+
const { source } = await client.getProgram(prog);
|
|
1212
1284
|
return textResult(`=== BOR ${name}.${method} (program: ${prog}, form: ${String(data.rows[0].FORMNAME ?? '').trim()}) ===\n${source}`);
|
|
1213
1285
|
}
|
|
1214
1286
|
return errorResult(`BOR method "${method}" not found on object type "${name}". Use SAPRead(type="SOBJ", name="${name}") without method to list all methods.`);
|
|
@@ -1280,18 +1352,8 @@ async function handleSAPRead(client, args, cachingLayer) {
|
|
|
1280
1352
|
return textResult(JSON.stringify(info, null, 2));
|
|
1281
1353
|
}
|
|
1282
1354
|
case 'INACTIVE_OBJECTS': {
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
return textResult(JSON.stringify({ count: objects.length, objects }, null, 2));
|
|
1286
|
-
}
|
|
1287
|
-
catch (err) {
|
|
1288
|
-
if (isNotFoundError(err)) {
|
|
1289
|
-
return textResult('Inactive objects listing is not available on this SAP system ' +
|
|
1290
|
-
'(the /sap/bc/adt/activation/inactive endpoint returned 404). ' +
|
|
1291
|
-
'Use SAPDiagnose(action="syntax", type="...", name="...") to check specific objects instead.');
|
|
1292
|
-
}
|
|
1293
|
-
throw err;
|
|
1294
|
-
}
|
|
1355
|
+
const objects = await client.getInactiveObjects();
|
|
1356
|
+
return textResult(JSON.stringify({ count: objects.length, objects }, null, 2));
|
|
1295
1357
|
}
|
|
1296
1358
|
default:
|
|
1297
1359
|
return errorResult(`Unknown SAPRead type: "${type}". Supported types: PROG, CLAS, INTF, FUNC, FUGR, INCL, DDLS, DCLS, DDLX, BDEF, SRVD, SRVB, SKTD, TABL, VIEW, STRU, DOMA, DTEL, AUTH, FTG2, ENHO, VERSIONS, VERSION_SOURCE, TRAN, TABLE_CONTENTS, DEVC, SOBJ, SYSTEM, COMPONENTS, MESSAGES, TEXT_ELEMENTS, VARIANTS, BSP, BSP_DEPLOY, API_STATE, INACTIVE_OBJECTS. ` +
|
|
@@ -1658,7 +1720,7 @@ async function mergeMetadataWriteProperties(client, type, name, provided) {
|
|
|
1658
1720
|
};
|
|
1659
1721
|
}
|
|
1660
1722
|
if (type === 'SRVB') {
|
|
1661
|
-
const existingRaw = await client.getSrvb(name);
|
|
1723
|
+
const { source: existingRaw } = await client.getSrvb(name);
|
|
1662
1724
|
const existing = JSON.parse(existingRaw);
|
|
1663
1725
|
return {
|
|
1664
1726
|
_description: existing.description,
|
|
@@ -2195,6 +2257,10 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2195
2257
|
}
|
|
2196
2258
|
const objectUrl = objectUrlForType(type, name);
|
|
2197
2259
|
const srcUrl = sourceUrlForType(type, name);
|
|
2260
|
+
const invalidateWrittenObject = (objType = type, objName = name) => {
|
|
2261
|
+
cachingLayer?.invalidate(objType, objName, 'all');
|
|
2262
|
+
cachingLayer?.inactiveLists.invalidate(client.username);
|
|
2263
|
+
};
|
|
2198
2264
|
// Helper: enforce allowedPackages for existing objects (update/delete/edit_method/scaffold_rap_handlers).
|
|
2199
2265
|
// Only fetches metadata when package restrictions are configured — no extra HTTP call otherwise.
|
|
2200
2266
|
async function enforcePackageForExistingObject() {
|
|
@@ -2215,10 +2281,10 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2215
2281
|
// no-ops (or 415s on strict systems). Fetch the current envelope,
|
|
2216
2282
|
// replace only the <sktd:text> body, and PUT it back — preserves
|
|
2217
2283
|
// responsible/masterLanguage/packageRef/refObject metadata.
|
|
2218
|
-
const currentEnvelope = await client.getKtd(name);
|
|
2284
|
+
const { source: currentEnvelope } = await client.getKtd(name);
|
|
2219
2285
|
const body = rewriteKtdText(currentEnvelope, source);
|
|
2220
2286
|
await safeUpdateObject(client.http, client.safety, objectUrl, body, SKTD_V2_CONTENT_TYPE, transport);
|
|
2221
|
-
|
|
2287
|
+
invalidateWrittenObject();
|
|
2222
2288
|
return textResult(`Successfully updated ${type} ${name}.`);
|
|
2223
2289
|
}
|
|
2224
2290
|
if (isMetadataWriteType(type)) {
|
|
@@ -2231,7 +2297,7 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2231
2297
|
const pkg = String(args.package ?? existingPackage ?? mergedProps._package ?? '$TMP');
|
|
2232
2298
|
const body = buildCreateXml(type, name, pkg, description, mergedProps);
|
|
2233
2299
|
await safeUpdateObject(client.http, client.safety, objectUrl, body, vendorContentTypeForType(type), transport);
|
|
2234
|
-
|
|
2300
|
+
invalidateWrittenObject();
|
|
2235
2301
|
return textResult(`Successfully updated ${type} ${name}.`);
|
|
2236
2302
|
}
|
|
2237
2303
|
// RAP deterministic preflight validation
|
|
@@ -2251,7 +2317,7 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2251
2317
|
// If safeUpdateSource throws (lock conflict, network error, etc.), checkNotes
|
|
2252
2318
|
// is intentionally discarded — pre-check warnings only matter when the write succeeded.
|
|
2253
2319
|
await safeUpdateSource(client.http, client.safety, objectUrl, srcUrl, source, transport);
|
|
2254
|
-
|
|
2320
|
+
invalidateWrittenObject();
|
|
2255
2321
|
const msg = `Successfully updated ${type} ${name}.`;
|
|
2256
2322
|
const cdsUpdateHint = type === 'DDLS' ? await buildCdsUpdateCrudHint(client, name, objectUrl) : undefined;
|
|
2257
2323
|
const warnings = mergePreWriteWarnings(preflightWarnings.warnings, lintWarnings.warnings, checkNotes, cdsUpdateHint);
|
|
@@ -2338,13 +2404,13 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2338
2404
|
// PUT back exactly the shape SAP gave us (with all the server-assigned
|
|
2339
2405
|
// metadata), only swapping <sktd:text>.
|
|
2340
2406
|
if (source) {
|
|
2341
|
-
const currentEnvelope = await client.getKtd(name);
|
|
2407
|
+
const { source: currentEnvelope } = await client.getKtd(name);
|
|
2342
2408
|
const body = rewriteKtdText(currentEnvelope, source);
|
|
2343
2409
|
await safeUpdateObject(client.http, client.safety, objectUrl, body, SKTD_V2_CONTENT_TYPE, effectiveTransport);
|
|
2344
|
-
|
|
2410
|
+
invalidateWrittenObject();
|
|
2345
2411
|
return textResult(`Created SKTD ${name} in package ${pkg} and wrote Markdown content.\nNext step: SAPActivate(type="SKTD", name="${name}").\n${ktdResult}`);
|
|
2346
2412
|
}
|
|
2347
|
-
|
|
2413
|
+
invalidateWrittenObject();
|
|
2348
2414
|
return textResult(`Created SKTD ${name} in package ${pkg} (no Markdown content written — pass "source" to write the body).\nNext step: SAPActivate(type="SKTD", name="${name}").\n${ktdResult}`);
|
|
2349
2415
|
}
|
|
2350
2416
|
// Build type-specific creation XML body.
|
|
@@ -2403,7 +2469,7 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2403
2469
|
}
|
|
2404
2470
|
});
|
|
2405
2471
|
}
|
|
2406
|
-
|
|
2472
|
+
invalidateWrittenObject();
|
|
2407
2473
|
const followUpHint = type === 'SRVB'
|
|
2408
2474
|
? `\n\nNext steps:\n1. SAPActivate(type="SRVB", name="${name}")\n2. SAPActivate(action="publish_srvb", name="${name}")`
|
|
2409
2475
|
: '';
|
|
@@ -2417,7 +2483,7 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2417
2483
|
return textResult(`Created ${type} ${name} in package ${pkg}, but source was rejected by lint:\n${lintWarnings.result.content[0].text}`);
|
|
2418
2484
|
}
|
|
2419
2485
|
await safeUpdateSource(client.http, client.safety, objectUrl, srcUrl, source, effectiveTransport);
|
|
2420
|
-
|
|
2486
|
+
invalidateWrittenObject();
|
|
2421
2487
|
const msg = `Created ${type} ${name} in package ${pkg} and wrote source code.`;
|
|
2422
2488
|
const warnings = mergePreWriteWarnings(preflightWarnings.warnings, lintWarnings.warnings);
|
|
2423
2489
|
return warnings ? textResult(`${msg}\n\n${warnings}`) : textResult(msg);
|
|
@@ -2435,8 +2501,8 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2435
2501
|
await enforcePackageForExistingObject();
|
|
2436
2502
|
// Fetch current full source (use cache if available)
|
|
2437
2503
|
const currentSource = cachingLayer
|
|
2438
|
-
? (await cachingLayer.getSource('CLAS', name, () => client.getClass(name))).source
|
|
2439
|
-
: await client.getClass(name);
|
|
2504
|
+
? (await cachingLayer.getSource('CLAS', name, (ifNoneMatch) => client.getClass(name, undefined, { ifNoneMatch }))).source
|
|
2505
|
+
: (await client.getClass(name)).source;
|
|
2440
2506
|
// Use detected ABAP version from probe if available
|
|
2441
2507
|
const abaplintVer = cachedFeatures?.abapRelease
|
|
2442
2508
|
? mapSapReleaseToAbaplintVersion(cachedFeatures.abapRelease)
|
|
@@ -2454,7 +2520,7 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2454
2520
|
const checkNotes = await runPreWriteSyntaxCheck(client, type, spliced.newSource, objectUrl, config, checkOverride);
|
|
2455
2521
|
// Write the full source back (existing lock/modify/unlock flow)
|
|
2456
2522
|
await safeUpdateSource(client.http, client.safety, objectUrl, srcUrl, spliced.newSource, transport);
|
|
2457
|
-
|
|
2523
|
+
invalidateWrittenObject();
|
|
2458
2524
|
const msg = `Successfully updated method "${method}" in ${type} ${name}.`;
|
|
2459
2525
|
const extras = [lintWarnings.warnings, checkNotes].filter(Boolean).join('\n\n');
|
|
2460
2526
|
return extras ? textResult(`${msg}\n\n${extras}`) : textResult(msg);
|
|
@@ -2510,8 +2576,9 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2510
2576
|
.filter(Boolean)
|
|
2511
2577
|
.join('\n\n');
|
|
2512
2578
|
const bdefSource = cachingLayer
|
|
2513
|
-
? (await cachingLayer.getSource('BDEF', bdefName, () => client.getBdef(bdefName)))
|
|
2514
|
-
|
|
2579
|
+
? (await cachingLayer.getSource('BDEF', bdefName, (ifNoneMatch) => client.getBdef(bdefName, { ifNoneMatch })))
|
|
2580
|
+
.source
|
|
2581
|
+
: (await client.getBdef(bdefName)).source;
|
|
2515
2582
|
let requirements = extractRapHandlerRequirements(bdefSource);
|
|
2516
2583
|
if (targetAlias) {
|
|
2517
2584
|
requirements = requirements.filter((req) => req.entityAlias.toLowerCase() === targetAlias);
|
|
@@ -2622,7 +2689,7 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2622
2689
|
}
|
|
2623
2690
|
}
|
|
2624
2691
|
});
|
|
2625
|
-
|
|
2692
|
+
invalidateWrittenObject();
|
|
2626
2693
|
const msg = `Scaffolded ${scaffoldPlan.insertedSignatureCount} RAP handler signature(s) and ${scaffoldPlan.insertedImplementationStubCount} implementation stub(s) in ${type} ${name} from BDEF ${bdefName}. ` +
|
|
2627
2694
|
`Updated section(s): ${scaffoldPlan.changedSections.join(', ')}.`;
|
|
2628
2695
|
const warnings = mergePreWriteWarnings(lintWarningsMain?.warnings, lintWarningsDefinitions?.warnings, lintWarningsImplementations?.warnings);
|
|
@@ -2672,7 +2739,7 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2672
2739
|
}
|
|
2673
2740
|
throw err;
|
|
2674
2741
|
}
|
|
2675
|
-
|
|
2742
|
+
invalidateWrittenObject();
|
|
2676
2743
|
return textResult(`Deleted ${type} ${name}.`);
|
|
2677
2744
|
}
|
|
2678
2745
|
case 'batch_create': {
|
|
@@ -2809,7 +2876,7 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
2809
2876
|
});
|
|
2810
2877
|
break;
|
|
2811
2878
|
}
|
|
2812
|
-
|
|
2879
|
+
invalidateWrittenObject(objType, objName);
|
|
2813
2880
|
results.push({ type: objType, name: objName, status: 'success' });
|
|
2814
2881
|
}
|
|
2815
2882
|
catch (err) {
|
|
@@ -3002,7 +3069,7 @@ async function runPreWriteSyntaxCheck(client, type, source, objectUrl, config, p
|
|
|
3002
3069
|
}
|
|
3003
3070
|
}
|
|
3004
3071
|
// ─── SAPActivate Handler ─────────────────────────────────────────────
|
|
3005
|
-
async function handleSAPActivate(client, args) {
|
|
3072
|
+
async function handleSAPActivate(client, args, cachingLayer) {
|
|
3006
3073
|
const action = String(args.action ?? 'activate');
|
|
3007
3074
|
const name = String(args.name ?? '');
|
|
3008
3075
|
const version = String(args.version ?? '0001');
|
|
@@ -3013,7 +3080,7 @@ async function handleSAPActivate(client, args) {
|
|
|
3013
3080
|
if (explicitServiceType === 'odatav4' || explicitServiceType === 'odatav2')
|
|
3014
3081
|
return explicitServiceType;
|
|
3015
3082
|
try {
|
|
3016
|
-
const srvbJson = await client.getSrvb(name);
|
|
3083
|
+
const { source: srvbJson } = await client.getSrvb(name);
|
|
3017
3084
|
const srvb = JSON.parse(srvbJson);
|
|
3018
3085
|
if (srvb.odataVersion === 'V4')
|
|
3019
3086
|
return 'odatav4';
|
|
@@ -3035,7 +3102,7 @@ async function handleSAPActivate(client, args) {
|
|
|
3035
3102
|
}
|
|
3036
3103
|
let srvbInfo;
|
|
3037
3104
|
try {
|
|
3038
|
-
srvbInfo = await client.getSrvb(name);
|
|
3105
|
+
srvbInfo = (await client.getSrvb(name)).source;
|
|
3039
3106
|
}
|
|
3040
3107
|
catch {
|
|
3041
3108
|
if (result.severity === 'UNKNOWN') {
|
|
@@ -3070,7 +3137,7 @@ async function handleSAPActivate(client, args) {
|
|
|
3070
3137
|
}
|
|
3071
3138
|
let srvbInfo;
|
|
3072
3139
|
try {
|
|
3073
|
-
srvbInfo = await client.getSrvb(name);
|
|
3140
|
+
srvbInfo = (await client.getSrvb(name)).source;
|
|
3074
3141
|
}
|
|
3075
3142
|
catch {
|
|
3076
3143
|
// Readback failed — fall through with what we have
|
|
@@ -3108,6 +3175,10 @@ async function handleSAPActivate(client, args) {
|
|
|
3108
3175
|
const batchStatuses = buildBatchActivationStatuses(objects, result);
|
|
3109
3176
|
const statusDetails = formatBatchActivationStatuses(batchStatuses);
|
|
3110
3177
|
if (result.success) {
|
|
3178
|
+
for (const object of objects) {
|
|
3179
|
+
cachingLayer?.invalidate(object.type, object.name, 'all');
|
|
3180
|
+
}
|
|
3181
|
+
cachingLayer?.inactiveLists.invalidate(client.username);
|
|
3111
3182
|
return textResult(`Successfully activated ${objects.length} objects: ${names}.${statusDetails}`);
|
|
3112
3183
|
}
|
|
3113
3184
|
// On batch failure enrich with per-object inactive-version syntax errors —
|
|
@@ -3124,6 +3195,8 @@ async function handleSAPActivate(client, args) {
|
|
|
3124
3195
|
const objectUrl = objectUrlForType(type, name);
|
|
3125
3196
|
const result = await activate(client.http, client.safety, objectUrl, { ...activateOpts, name });
|
|
3126
3197
|
if (result.success) {
|
|
3198
|
+
cachingLayer?.invalidate(type, name, 'all');
|
|
3199
|
+
cachingLayer?.inactiveLists.invalidate(client.username);
|
|
3127
3200
|
return textResult(`Successfully activated ${type} ${name}.${formatActivationMessages(result)}`);
|
|
3128
3201
|
}
|
|
3129
3202
|
// On failure, try to enrich with the actual compiler errors from the inactive version —
|
|
@@ -3973,7 +4046,7 @@ async function handleSAPContext(client, args, cachingLayer) {
|
|
|
3973
4046
|
// Helper: get source with cache support
|
|
3974
4047
|
const cachedGet = async (objType, objName, fetcher) => {
|
|
3975
4048
|
if (!cachingLayer)
|
|
3976
|
-
return fetcher();
|
|
4049
|
+
return (await fetcher()).source;
|
|
3977
4050
|
const { source } = await cachingLayer.getSource(objType, objName, fetcher);
|
|
3978
4051
|
return source;
|
|
3979
4052
|
};
|
|
@@ -3981,7 +4054,7 @@ async function handleSAPContext(client, args, cachingLayer) {
|
|
|
3981
4054
|
if (type !== 'DDLS') {
|
|
3982
4055
|
return errorResult('SAPContext(action="impact") supports DDLS only. For non-CDS objects, use SAPNavigate(action="references").');
|
|
3983
4056
|
}
|
|
3984
|
-
const ddlSource = await cachedGet('DDLS', name, () => client.getDdls(name));
|
|
4057
|
+
const ddlSource = await cachedGet('DDLS', name, (ifNoneMatch) => client.getDdls(name, { ifNoneMatch }));
|
|
3985
4058
|
const upstream = buildCdsUpstream(extractCdsDependencies(ddlSource));
|
|
3986
4059
|
const includeIndirect = args.includeIndirect === true;
|
|
3987
4060
|
const siblingCheck = args.siblingCheck !== false;
|
|
@@ -4146,24 +4219,24 @@ async function handleSAPContext(client, args, cachingLayer) {
|
|
|
4146
4219
|
else {
|
|
4147
4220
|
switch (type) {
|
|
4148
4221
|
case 'CLAS':
|
|
4149
|
-
source = await cachedGet('CLAS', name, () => client.getClass(name));
|
|
4222
|
+
source = await cachedGet('CLAS', name, (ifNoneMatch) => client.getClass(name, undefined, { ifNoneMatch }));
|
|
4150
4223
|
break;
|
|
4151
4224
|
case 'INTF':
|
|
4152
|
-
source = await cachedGet('INTF', name, () => client.getInterface(name));
|
|
4225
|
+
source = await cachedGet('INTF', name, (ifNoneMatch) => client.getInterface(name, { ifNoneMatch }));
|
|
4153
4226
|
break;
|
|
4154
4227
|
case 'PROG':
|
|
4155
|
-
source = await cachedGet('PROG', name, () => client.getProgram(name));
|
|
4228
|
+
source = await cachedGet('PROG', name, (ifNoneMatch) => client.getProgram(name, { ifNoneMatch }));
|
|
4156
4229
|
break;
|
|
4157
4230
|
case 'FUNC': {
|
|
4158
4231
|
const group = String(args.group ?? '');
|
|
4159
4232
|
if (!group) {
|
|
4160
4233
|
return errorResult('The "group" parameter is required for FUNC type. Use SAPSearch to find the function group.');
|
|
4161
4234
|
}
|
|
4162
|
-
source = await cachedGet('FUNC', name, () => client.getFunction(group, name));
|
|
4235
|
+
source = await cachedGet('FUNC', name, (ifNoneMatch) => client.getFunction(group, name, { ifNoneMatch }));
|
|
4163
4236
|
break;
|
|
4164
4237
|
}
|
|
4165
4238
|
case 'DDLS': {
|
|
4166
|
-
const ddlSource = await cachedGet('DDLS', name, () => client.getDdls(name));
|
|
4239
|
+
const ddlSource = await cachedGet('DDLS', name, (ifNoneMatch) => client.getDdls(name, { ifNoneMatch }));
|
|
4167
4240
|
const cdsResult = await compressCdsContext(client, ddlSource, name, maxDeps, depth, cachingLayer);
|
|
4168
4241
|
return textResult(cdsResult.output);
|
|
4169
4242
|
}
|
|
@@ -4536,6 +4609,7 @@ async function handleSAPManage(client, config, args, cachingLayer, isPerUserClie
|
|
|
4536
4609
|
enabled: true,
|
|
4537
4610
|
warmupAvailable: cachingLayer.isWarmupAvailable,
|
|
4538
4611
|
...stats,
|
|
4612
|
+
inactiveListCache: cachingLayer.inactiveLists.stats(),
|
|
4539
4613
|
}, null, 2));
|
|
4540
4614
|
}
|
|
4541
4615
|
case 'probe': {
|