arc-1 0.4.4 → 0.5.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 +2 -1
- package/dist/adt/client.d.ts +6 -0
- package/dist/adt/client.d.ts.map +1 -1
- package/dist/adt/client.js +12 -0
- package/dist/adt/client.js.map +1 -1
- package/dist/adt/codeintel.js +1 -1
- package/dist/adt/codeintel.js.map +1 -1
- package/dist/adt/features.d.ts +29 -1
- package/dist/adt/features.d.ts.map +1 -1
- package/dist/adt/features.js +114 -2
- package/dist/adt/features.js.map +1 -1
- package/dist/adt/http.d.ts +24 -1
- package/dist/adt/http.d.ts.map +1 -1
- package/dist/adt/http.js +87 -28
- package/dist/adt/http.js.map +1 -1
- package/dist/adt/oauth.d.ts +19 -2
- package/dist/adt/oauth.d.ts.map +1 -1
- package/dist/adt/oauth.js +78 -28
- package/dist/adt/oauth.js.map +1 -1
- package/dist/adt/safety.d.ts +15 -0
- package/dist/adt/safety.d.ts.map +1 -1
- package/dist/adt/safety.js +50 -0
- package/dist/adt/safety.js.map +1 -1
- package/dist/adt/types.d.ts +14 -0
- package/dist/adt/types.d.ts.map +1 -1
- package/dist/handlers/hyperfocused.d.ts +1 -0
- package/dist/handlers/hyperfocused.d.ts.map +1 -1
- package/dist/handlers/hyperfocused.js +6 -5
- package/dist/handlers/hyperfocused.js.map +1 -1
- package/dist/handlers/intent.d.ts +17 -1
- package/dist/handlers/intent.d.ts.map +1 -1
- package/dist/handlers/intent.js +250 -25
- package/dist/handlers/intent.js.map +1 -1
- package/dist/handlers/schemas.d.ts +255 -0
- package/dist/handlers/schemas.d.ts.map +1 -0
- package/dist/handlers/schemas.js +232 -0
- package/dist/handlers/schemas.js.map +1 -0
- package/dist/handlers/tools.d.ts +1 -1
- package/dist/handlers/tools.d.ts.map +1 -1
- package/dist/handlers/tools.js +64 -32
- package/dist/handlers/tools.js.map +1 -1
- package/dist/handlers/zod-errors.d.ts +20 -0
- package/dist/handlers/zod-errors.d.ts.map +1 -0
- package/dist/handlers/zod-errors.js +43 -0
- package/dist/handlers/zod-errors.js.map +1 -0
- package/dist/server/config.d.ts +26 -0
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/config.js +162 -5
- package/dist/server/config.js.map +1 -1
- package/dist/server/http.d.ts +8 -0
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +134 -71
- package/dist/server/http.js.map +1 -1
- package/dist/server/server.d.ts +13 -2
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +82 -8
- package/dist/server/server.js.map +1 -1
- package/dist/server/types.d.ts +8 -0
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +1 -0
- package/dist/server/types.js.map +1 -1
- package/dist/server/xsuaa.d.ts +11 -1
- package/dist/server/xsuaa.d.ts.map +1 -1
- package/dist/server/xsuaa.js +127 -9
- package/dist/server/xsuaa.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hyperfocused.js","sourceRoot":"","sources":["../../src/handlers/hyperfocused.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,oDAAoD;AACpD,MAAM,cAAc,GAA2B;IAC7C,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,WAAW;IACnB,KAAK,EAAE,UAAU;IACjB,KAAK,EAAE,UAAU;IACjB,QAAQ,EAAE,aAAa;IACvB,QAAQ,EAAE,aAAa;IACvB,IAAI,EAAE,SAAS;IACf,QAAQ,EAAE,aAAa;IACvB,SAAS,EAAE,cAAc;IACzB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;CACpB,CAAC;AAEF,uCAAuC;AACvC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"hyperfocused.js","sourceRoot":"","sources":["../../src/handlers/hyperfocused.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,6 +34,12 @@ export interface ToolResult {
|
|
|
34
34
|
* A user with `write` scope but `readOnly=true` in config still can't write.
|
|
35
35
|
*/
|
|
36
36
|
export declare const TOOL_SCOPES: Record<string, string>;
|
|
37
|
+
/**
|
|
38
|
+
* Check if authInfo has the required scope, respecting implied scopes:
|
|
39
|
+
* - `write` implies `read`
|
|
40
|
+
* - `sql` implies `data`
|
|
41
|
+
*/
|
|
42
|
+
export declare function hasRequiredScope(authInfo: AuthInfo, requiredScope: string): boolean;
|
|
37
43
|
/**
|
|
38
44
|
* Handle an MCP tool call.
|
|
39
45
|
*
|
|
@@ -42,9 +48,19 @@ export declare const TOOL_SCOPES: Record<string, string>;
|
|
|
42
48
|
* all tools are allowed (backward compatibility).
|
|
43
49
|
* @param server - MCP Server instance for elicitation support.
|
|
44
50
|
*/
|
|
45
|
-
export declare function handleToolCall(client: AdtClient, config: ServerConfig, toolName: string, args: Record<string, unknown>, authInfo?: AuthInfo, _server?: Server, cachingLayer?: CachingLayer): Promise<ToolResult>;
|
|
51
|
+
export declare function handleToolCall(client: AdtClient, config: ServerConfig, toolName: string, args: Record<string, unknown>, authInfo?: AuthInfo, _server?: Server, cachingLayer?: CachingLayer, isPerUserClient?: boolean): Promise<ToolResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Build the type-specific XML body for ADT object creation.
|
|
54
|
+
*
|
|
55
|
+
* SAP ADT requires each object type to have its own root XML element.
|
|
56
|
+
* Using a generic body (e.g. adtcore:objectReferences) returns 400:
|
|
57
|
+
* "System expected the element '{http://www.sap.com/adt/programs/programs}abapProgram'"
|
|
58
|
+
*/
|
|
59
|
+
export declare function buildCreateXml(type: string, name: string, pkg: string, description: string): string;
|
|
46
60
|
/** Reset cached features (for testing) */
|
|
47
61
|
export declare function resetCachedFeatures(): void;
|
|
48
62
|
/** Set cached features directly (for testing BTP mode, etc.) */
|
|
49
63
|
export declare function setCachedFeatures(features: ResolvedFeatures | undefined): void;
|
|
64
|
+
/** Get cached features (for tool definition adaptation) */
|
|
65
|
+
export declare function getCachedFeatures(): ResolvedFeatures | undefined;
|
|
50
66
|
//# sourceMappingURL=intent.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../src/handlers/intent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gDAAgD,CAAC;AAC/E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAuBlD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAc9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../src/handlers/intent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gDAAgD,CAAC;AAC/E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAuBlD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAc9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAKvD,2BAA2B;AAC3B,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAY9C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CASnF;AAuCD;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,QAAQ,CAAC,EAAE,QAAQ,EACnB,OAAO,CAAC,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,YAAY,EAC3B,eAAe,CAAC,EAAE,OAAO,GACxB,OAAO,CAAC,UAAU,CAAC,CAqLrB;AAyYD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAyGnG;AAkqBD,0CAA0C;AAC1C,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,SAAS,GAAG,IAAI,CAE9E;AAED,2DAA2D;AAC3D,wBAAgB,iBAAiB,IAAI,gBAAgB,GAAG,SAAS,CAEhE"}
|
package/dist/handlers/intent.js
CHANGED
|
@@ -13,8 +13,8 @@ import { findDefinition, findReferences, findWhereUsed, getCompletion, } from '.
|
|
|
13
13
|
import { createObject, deleteObject, lockObject, safeUpdateSource, unlockObject } from '../adt/crud.js';
|
|
14
14
|
import { activate, activateBatch, runAtcCheck, runUnitTests, syntaxCheck } from '../adt/devtools.js';
|
|
15
15
|
import { getDump, getTraceDbAccesses, getTraceHitlist, getTraceStatements, listDumps, listTraces, } from '../adt/diagnostics.js';
|
|
16
|
-
import { AdtApiError, AdtNetworkError, AdtSafetyError } from '../adt/errors.js';
|
|
17
|
-
import { mapSapReleaseToAbaplintVersion, probeFeatures } from '../adt/features.js';
|
|
16
|
+
import { AdtApiError, AdtNetworkError, AdtSafetyError, isNotFoundError } from '../adt/errors.js';
|
|
17
|
+
import { classifyTextSearchError, mapSapReleaseToAbaplintVersion, probeFeatures } from '../adt/features.js';
|
|
18
18
|
import { isOperationAllowed, OperationType } from '../adt/safety.js';
|
|
19
19
|
import { createTransport, getTransport, listTransports, releaseTransport } from '../adt/transport.js';
|
|
20
20
|
import { extractCdsElements } from '../context/cds-deps.js';
|
|
@@ -26,6 +26,8 @@ import { sanitizeArgs } from '../server/audit.js';
|
|
|
26
26
|
import { generateRequestId, requestContext } from '../server/context.js';
|
|
27
27
|
import { logger } from '../server/logger.js';
|
|
28
28
|
import { expandHyperfocusedArgs, getHyperfocusedScope } from './hyperfocused.js';
|
|
29
|
+
import { getToolSchema } from './schemas.js';
|
|
30
|
+
import { formatZodError } from './zod-errors.js';
|
|
29
31
|
/**
|
|
30
32
|
* Scope required for each tool.
|
|
31
33
|
*
|
|
@@ -39,7 +41,7 @@ import { expandHyperfocusedArgs, getHyperfocusedScope } from './hyperfocused.js'
|
|
|
39
41
|
export const TOOL_SCOPES = {
|
|
40
42
|
SAPRead: 'read',
|
|
41
43
|
SAPSearch: 'read',
|
|
42
|
-
SAPQuery: '
|
|
44
|
+
SAPQuery: 'sql',
|
|
43
45
|
SAPNavigate: 'read',
|
|
44
46
|
SAPContext: 'read',
|
|
45
47
|
SAPLint: 'read',
|
|
@@ -47,8 +49,24 @@ export const TOOL_SCOPES = {
|
|
|
47
49
|
SAPWrite: 'write',
|
|
48
50
|
SAPActivate: 'write',
|
|
49
51
|
SAPManage: 'write',
|
|
50
|
-
SAPTransport: '
|
|
52
|
+
SAPTransport: 'write',
|
|
51
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Check if authInfo has the required scope, respecting implied scopes:
|
|
56
|
+
* - `write` implies `read`
|
|
57
|
+
* - `sql` implies `data`
|
|
58
|
+
*/
|
|
59
|
+
export function hasRequiredScope(authInfo, requiredScope) {
|
|
60
|
+
const scopes = authInfo.scopes;
|
|
61
|
+
if (scopes.includes(requiredScope))
|
|
62
|
+
return true;
|
|
63
|
+
// Implied scopes
|
|
64
|
+
if (requiredScope === 'read' && scopes.includes('write'))
|
|
65
|
+
return true;
|
|
66
|
+
if (requiredScope === 'data' && scopes.includes('sql'))
|
|
67
|
+
return true;
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
52
70
|
function textResult(text) {
|
|
53
71
|
return { content: [{ type: 'text', text }] };
|
|
54
72
|
}
|
|
@@ -92,7 +110,7 @@ function classifyError(err) {
|
|
|
92
110
|
* all tools are allowed (backward compatibility).
|
|
93
111
|
* @param server - MCP Server instance for elicitation support.
|
|
94
112
|
*/
|
|
95
|
-
export async function handleToolCall(client, config, toolName, args, authInfo, _server, cachingLayer) {
|
|
113
|
+
export async function handleToolCall(client, config, toolName, args, authInfo, _server, cachingLayer, isPerUserClient) {
|
|
96
114
|
const reqId = generateRequestId();
|
|
97
115
|
const start = Date.now();
|
|
98
116
|
// Build user context for audit logging
|
|
@@ -112,7 +130,7 @@ export async function handleToolCall(client, config, toolName, args, authInfo, _
|
|
|
112
130
|
// Scope enforcement — only when authInfo is present (XSUAA/OIDC mode)
|
|
113
131
|
if (authInfo) {
|
|
114
132
|
const requiredScope = TOOL_SCOPES[toolName];
|
|
115
|
-
if (requiredScope && !authInfo
|
|
133
|
+
if (requiredScope && !hasRequiredScope(authInfo, requiredScope)) {
|
|
116
134
|
logger.emitAudit({
|
|
117
135
|
timestamp: new Date().toISOString(),
|
|
118
136
|
level: 'warn',
|
|
@@ -127,6 +145,29 @@ export async function handleToolCall(client, config, toolName, args, authInfo, _
|
|
|
127
145
|
return errorResult(`Insufficient scope: '${requiredScope}' required for ${toolName}. Your scopes: [${authInfo.scopes.join(', ')}]`);
|
|
128
146
|
}
|
|
129
147
|
}
|
|
148
|
+
// Validate tool arguments with Zod schema
|
|
149
|
+
const isBtp = config.systemType === 'btp';
|
|
150
|
+
// Always use the full search schema for validation — the handler checks text search availability
|
|
151
|
+
// and returns a proper error message with the probe reason when source_code search is unavailable
|
|
152
|
+
const schema = getToolSchema(toolName, isBtp);
|
|
153
|
+
if (schema) {
|
|
154
|
+
const parsed = schema.safeParse(args);
|
|
155
|
+
if (!parsed.success) {
|
|
156
|
+
const validationError = formatZodError(parsed.error, toolName);
|
|
157
|
+
logger.emitAudit({
|
|
158
|
+
timestamp: new Date().toISOString(),
|
|
159
|
+
level: 'warn',
|
|
160
|
+
event: 'safety_blocked',
|
|
161
|
+
requestId: reqId,
|
|
162
|
+
user,
|
|
163
|
+
clientId,
|
|
164
|
+
operation: toolName,
|
|
165
|
+
reason: 'Input validation failed',
|
|
166
|
+
});
|
|
167
|
+
return errorResult(validationError);
|
|
168
|
+
}
|
|
169
|
+
args = parsed.data;
|
|
170
|
+
}
|
|
130
171
|
// Run within request context so HTTP-level logs get the requestId
|
|
131
172
|
return requestContext.run({ requestId: reqId, user, tool: toolName }, async () => {
|
|
132
173
|
try {
|
|
@@ -163,7 +204,7 @@ export async function handleToolCall(client, config, toolName, args, authInfo, _
|
|
|
163
204
|
result = await handleSAPContext(client, args, cachingLayer);
|
|
164
205
|
break;
|
|
165
206
|
case 'SAPManage':
|
|
166
|
-
result = await handleSAPManage(client, config, args, cachingLayer);
|
|
207
|
+
result = await handleSAPManage(client, config, args, cachingLayer, isPerUserClient);
|
|
167
208
|
break;
|
|
168
209
|
case 'SAP': {
|
|
169
210
|
// Hyperfocused mode: route to the appropriate handler
|
|
@@ -175,13 +216,13 @@ export async function handleToolCall(client, config, toolName, args, authInfo, _
|
|
|
175
216
|
// Check scope for the delegated action
|
|
176
217
|
if (authInfo) {
|
|
177
218
|
const requiredScope = getHyperfocusedScope(String(args.action ?? ''));
|
|
178
|
-
if (!authInfo
|
|
219
|
+
if (!hasRequiredScope(authInfo, requiredScope)) {
|
|
179
220
|
result = errorResult(`Insufficient scope: '${requiredScope}' required for SAP(action="${args.action}"). Your scopes: [${authInfo.scopes.join(', ')}]`);
|
|
180
221
|
break;
|
|
181
222
|
}
|
|
182
223
|
}
|
|
183
224
|
// Delegate to the real handler (recursive call, but with the mapped tool name)
|
|
184
|
-
result = await handleToolCall(client, config, expanded.toolName, expanded.expandedArgs, authInfo, _server, cachingLayer);
|
|
225
|
+
result = await handleToolCall(client, config, expanded.toolName, expanded.expandedArgs, authInfo, _server, cachingLayer, isPerUserClient);
|
|
185
226
|
break;
|
|
186
227
|
}
|
|
187
228
|
default:
|
|
@@ -336,8 +377,17 @@ async function handleSAPRead(client, args, cachingLayer) {
|
|
|
336
377
|
return textResult(await cachedGet('BDEF', name, () => client.getBdef(name)));
|
|
337
378
|
case 'SRVD':
|
|
338
379
|
return textResult(await cachedGet('SRVD', name, () => client.getSrvd(name)));
|
|
339
|
-
case 'DDLX':
|
|
340
|
-
|
|
380
|
+
case 'DDLX': {
|
|
381
|
+
try {
|
|
382
|
+
return textResult(await cachedGet('DDLX', name, () => client.getDdlx(name)));
|
|
383
|
+
}
|
|
384
|
+
catch (err) {
|
|
385
|
+
if (isNotFoundError(err)) {
|
|
386
|
+
return textResult(`No metadata extension (DDLX) found for "${name}". This means no @UI annotations are defined via DDLX for this view. The view may use inline annotations in the DDLS source, or the Fiori app may configure columns via manifest.json / app descriptor.`);
|
|
387
|
+
}
|
|
388
|
+
throw err;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
341
391
|
case 'SRVB':
|
|
342
392
|
return textResult(await cachedGet('SRVB', name, () => client.getSrvb(name)));
|
|
343
393
|
case 'TABL':
|
|
@@ -421,7 +471,9 @@ async function handleSAPRead(client, args, cachingLayer) {
|
|
|
421
471
|
case 'VARIANTS':
|
|
422
472
|
return textResult(await client.getVariants(name));
|
|
423
473
|
default:
|
|
424
|
-
return errorResult(`Unknown SAPRead type: ${type}. Supported: PROG, CLAS, INTF, FUNC, FUGR, INCL, DDLS, DDLX, BDEF, SRVD, SRVB, TABL, VIEW, STRU, DOMA, DTEL, TRAN, TABLE_CONTENTS, DEVC, SOBJ, SYSTEM, COMPONENTS, MESSAGES, TEXT_ELEMENTS, VARIANTS`
|
|
474
|
+
return errorResult(`Unknown SAPRead type: "${type}". Supported types: PROG, CLAS, INTF, FUNC, FUGR, INCL, DDLS, DDLX, BDEF, SRVD, SRVB, TABL, VIEW, STRU, DOMA, DTEL, TRAN, TABLE_CONTENTS, DEVC, SOBJ, SYSTEM, COMPONENTS, MESSAGES, TEXT_ELEMENTS, VARIANTS. ` +
|
|
475
|
+
'Tip: Map objectType from SAPSearch results by dropping the slash suffix (e.g., DDLS/DF → type="DDLS", CLAS/OC → type="CLAS", PROG/P → type="PROG"). ' +
|
|
476
|
+
'Do not pass a URI — use the "type" and "name" parameters instead.');
|
|
425
477
|
}
|
|
426
478
|
}
|
|
427
479
|
async function handleSAPSearch(client, args) {
|
|
@@ -429,6 +481,11 @@ async function handleSAPSearch(client, args) {
|
|
|
429
481
|
const maxResults = Number(args.maxResults ?? 100);
|
|
430
482
|
const searchType = String(args.searchType ?? 'object');
|
|
431
483
|
if (searchType === 'source_code') {
|
|
484
|
+
// If probe already determined textSearch is unavailable, return the precise reason
|
|
485
|
+
if (cachedFeatures?.textSearch && !cachedFeatures.textSearch.available) {
|
|
486
|
+
return errorResult(`Source code search is not available on this SAP system. ${cachedFeatures.textSearch.reason ?? ''}` +
|
|
487
|
+
`\nUse SAPSearch with searchType="object" to search by object name instead, or use SAPQuery to search metadata tables.`);
|
|
488
|
+
}
|
|
432
489
|
const objectType = args.objectType;
|
|
433
490
|
const packageName = args.packageName;
|
|
434
491
|
try {
|
|
@@ -436,9 +493,13 @@ async function handleSAPSearch(client, args) {
|
|
|
436
493
|
return textResult(JSON.stringify(results, null, 2));
|
|
437
494
|
}
|
|
438
495
|
catch (err) {
|
|
439
|
-
if (err instanceof AdtApiError
|
|
440
|
-
|
|
441
|
-
|
|
496
|
+
if (err instanceof AdtApiError) {
|
|
497
|
+
const permanentCodes = [401, 403, 404, 501];
|
|
498
|
+
if (permanentCodes.includes(err.statusCode)) {
|
|
499
|
+
const classified = classifyTextSearchError(err.statusCode);
|
|
500
|
+
return errorResult(`Source code search is not available on this SAP system. ${classified.reason ?? ''}` +
|
|
501
|
+
`\nUse SAPSearch with searchType="object" to search by object name instead, or use SAPQuery to search metadata tables.`);
|
|
502
|
+
}
|
|
442
503
|
}
|
|
443
504
|
throw err;
|
|
444
505
|
}
|
|
@@ -474,6 +535,10 @@ async function handleSAPQuery(client, args) {
|
|
|
474
535
|
}
|
|
475
536
|
}
|
|
476
537
|
}
|
|
538
|
+
// JOIN-aware error: ADT freestyle SQL parser has known edge cases with JOINs (SAP Note 3605050)
|
|
539
|
+
if (err instanceof AdtApiError && err.statusCode === 400 && /\bJOIN\b/i.test(sql)) {
|
|
540
|
+
return errorResult(`${err.message}\n\nMulti-table JOIN query failed. The ADT freestyle SQL endpoint has known parser edge cases with JOINs (SAP Note 3605050). Try splitting into separate single-table queries.`);
|
|
541
|
+
}
|
|
477
542
|
throw err;
|
|
478
543
|
}
|
|
479
544
|
}
|
|
@@ -540,6 +605,129 @@ function buildLintConfigOptions(config, ruleOverrides) {
|
|
|
540
605
|
ruleOverrides,
|
|
541
606
|
};
|
|
542
607
|
}
|
|
608
|
+
// ─── Object Creation XML ─────────────────────────────────────────────
|
|
609
|
+
/**
|
|
610
|
+
* Build the type-specific XML body for ADT object creation.
|
|
611
|
+
*
|
|
612
|
+
* SAP ADT requires each object type to have its own root XML element.
|
|
613
|
+
* Using a generic body (e.g. adtcore:objectReferences) returns 400:
|
|
614
|
+
* "System expected the element '{http://www.sap.com/adt/programs/programs}abapProgram'"
|
|
615
|
+
*/
|
|
616
|
+
export function buildCreateXml(type, name, pkg, description) {
|
|
617
|
+
switch (type) {
|
|
618
|
+
case 'PROG':
|
|
619
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
620
|
+
<program:abapProgram xmlns:program="http://www.sap.com/adt/programs/programs"
|
|
621
|
+
xmlns:adtcore="http://www.sap.com/adt/core"
|
|
622
|
+
adtcore:description="${escapeXml(description)}"
|
|
623
|
+
adtcore:name="${escapeXml(name)}"
|
|
624
|
+
adtcore:type="PROG/P"
|
|
625
|
+
adtcore:masterLanguage="EN"
|
|
626
|
+
adtcore:masterSystem="H00"
|
|
627
|
+
adtcore:responsible="DEVELOPER">
|
|
628
|
+
<adtcore:packageRef adtcore:name="${escapeXml(pkg)}"/>
|
|
629
|
+
</program:abapProgram>`;
|
|
630
|
+
case 'CLAS':
|
|
631
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
632
|
+
<class:abapClass xmlns:class="http://www.sap.com/adt/oo/classes"
|
|
633
|
+
xmlns:adtcore="http://www.sap.com/adt/core"
|
|
634
|
+
adtcore:description="${escapeXml(description)}"
|
|
635
|
+
adtcore:name="${escapeXml(name)}"
|
|
636
|
+
adtcore:type="CLAS/OC"
|
|
637
|
+
adtcore:masterLanguage="EN"
|
|
638
|
+
adtcore:masterSystem="H00"
|
|
639
|
+
adtcore:responsible="DEVELOPER">
|
|
640
|
+
<adtcore:packageRef adtcore:name="${escapeXml(pkg)}"/>
|
|
641
|
+
</class:abapClass>`;
|
|
642
|
+
case 'INTF':
|
|
643
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
644
|
+
<intf:abapInterface xmlns:intf="http://www.sap.com/adt/oo/interfaces"
|
|
645
|
+
xmlns:adtcore="http://www.sap.com/adt/core"
|
|
646
|
+
adtcore:description="${escapeXml(description)}"
|
|
647
|
+
adtcore:name="${escapeXml(name)}"
|
|
648
|
+
adtcore:type="INTF/OI"
|
|
649
|
+
adtcore:masterLanguage="EN"
|
|
650
|
+
adtcore:masterSystem="H00"
|
|
651
|
+
adtcore:responsible="DEVELOPER">
|
|
652
|
+
<adtcore:packageRef adtcore:name="${escapeXml(pkg)}"/>
|
|
653
|
+
</intf:abapInterface>`;
|
|
654
|
+
case 'INCL':
|
|
655
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
656
|
+
<include:abapInclude xmlns:include="http://www.sap.com/adt/programs/includes"
|
|
657
|
+
xmlns:adtcore="http://www.sap.com/adt/core"
|
|
658
|
+
adtcore:description="${escapeXml(description)}"
|
|
659
|
+
adtcore:name="${escapeXml(name)}"
|
|
660
|
+
adtcore:type="PROG/I"
|
|
661
|
+
adtcore:masterLanguage="EN"
|
|
662
|
+
adtcore:masterSystem="H00"
|
|
663
|
+
adtcore:responsible="DEVELOPER">
|
|
664
|
+
<adtcore:packageRef adtcore:name="${escapeXml(pkg)}"/>
|
|
665
|
+
</include:abapInclude>`;
|
|
666
|
+
case 'DDLS':
|
|
667
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
668
|
+
<ddl:ddlSource xmlns:ddl="http://www.sap.com/adt/ddic/ddlsources"
|
|
669
|
+
xmlns:adtcore="http://www.sap.com/adt/core"
|
|
670
|
+
adtcore:description="${escapeXml(description)}"
|
|
671
|
+
adtcore:name="${escapeXml(name)}"
|
|
672
|
+
adtcore:type="DDLS/DF"
|
|
673
|
+
adtcore:masterLanguage="EN"
|
|
674
|
+
adtcore:masterSystem="H00"
|
|
675
|
+
adtcore:responsible="DEVELOPER">
|
|
676
|
+
<adtcore:packageRef adtcore:name="${escapeXml(pkg)}"/>
|
|
677
|
+
</ddl:ddlSource>`;
|
|
678
|
+
case 'BDEF':
|
|
679
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
680
|
+
<bdef:behaviorDefinition xmlns:bdef="http://www.sap.com/adt/bo/behaviordefinitions"
|
|
681
|
+
xmlns:adtcore="http://www.sap.com/adt/core"
|
|
682
|
+
adtcore:description="${escapeXml(description)}"
|
|
683
|
+
adtcore:name="${escapeXml(name)}"
|
|
684
|
+
adtcore:type="BDEF/BDO"
|
|
685
|
+
adtcore:masterLanguage="EN"
|
|
686
|
+
adtcore:masterSystem="H00"
|
|
687
|
+
adtcore:responsible="DEVELOPER">
|
|
688
|
+
<adtcore:packageRef adtcore:name="${escapeXml(pkg)}"/>
|
|
689
|
+
</bdef:behaviorDefinition>`;
|
|
690
|
+
case 'SRVD':
|
|
691
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
692
|
+
<srvd:srvdSource xmlns:srvd="http://www.sap.com/adt/ddic/srvd/sources"
|
|
693
|
+
xmlns:adtcore="http://www.sap.com/adt/core"
|
|
694
|
+
adtcore:description="${escapeXml(description)}"
|
|
695
|
+
adtcore:name="${escapeXml(name)}"
|
|
696
|
+
adtcore:type="SRVD/SRV"
|
|
697
|
+
adtcore:masterLanguage="EN"
|
|
698
|
+
adtcore:masterSystem="H00"
|
|
699
|
+
adtcore:responsible="DEVELOPER">
|
|
700
|
+
<adtcore:packageRef adtcore:name="${escapeXml(pkg)}"/>
|
|
701
|
+
</srvd:srvdSource>`;
|
|
702
|
+
case 'DDLX':
|
|
703
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
704
|
+
<ddlx:ddlxSource xmlns:ddlx="http://www.sap.com/adt/ddic/ddlx/sources"
|
|
705
|
+
xmlns:adtcore="http://www.sap.com/adt/core"
|
|
706
|
+
adtcore:description="${escapeXml(description)}"
|
|
707
|
+
adtcore:name="${escapeXml(name)}"
|
|
708
|
+
adtcore:type="DDLX/EX"
|
|
709
|
+
adtcore:masterLanguage="EN"
|
|
710
|
+
adtcore:masterSystem="H00"
|
|
711
|
+
adtcore:responsible="DEVELOPER">
|
|
712
|
+
<adtcore:packageRef adtcore:name="${escapeXml(pkg)}"/>
|
|
713
|
+
</ddlx:ddlxSource>`;
|
|
714
|
+
default:
|
|
715
|
+
// Fallback — generic objectReferences using the correct URL for the type
|
|
716
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
717
|
+
<adtcore:objectReferences xmlns:adtcore="http://www.sap.com/adt/core">
|
|
718
|
+
<adtcore:objectReference adtcore:uri="${escapeXml(objectUrlForType(type, name))}" adtcore:type="${escapeXml(type)}" adtcore:name="${escapeXml(name)}" adtcore:packageName="${escapeXml(pkg)}"/>
|
|
719
|
+
</adtcore:objectReferences>`;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
/** Escape special characters for XML attribute values */
|
|
723
|
+
function escapeXml(s) {
|
|
724
|
+
return s
|
|
725
|
+
.replace(/&/g, '&')
|
|
726
|
+
.replace(/"/g, '"')
|
|
727
|
+
.replace(/'/g, ''')
|
|
728
|
+
.replace(/</g, '<')
|
|
729
|
+
.replace(/>/g, '>');
|
|
730
|
+
}
|
|
543
731
|
// ─── Object URL Mapping ──────────────────────────────────────────────
|
|
544
732
|
/** Map object type + name to the ADT object URL used by CRUD/DevTools/etc. */
|
|
545
733
|
function objectUrlForType(type, name) {
|
|
@@ -607,12 +795,26 @@ async function handleSAPWrite(client, args, config, cachingLayer) {
|
|
|
607
795
|
}
|
|
608
796
|
case 'create': {
|
|
609
797
|
const pkg = String(args.package ?? '$TMP');
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
798
|
+
const description = String(args.description ?? name);
|
|
799
|
+
// Build type-specific creation XML body.
|
|
800
|
+
// SAP ADT requires the root element to match the object type —
|
|
801
|
+
// a generic objectReferences body returns 400 "System expected the element ...".
|
|
802
|
+
const body = buildCreateXml(type, name, pkg, description);
|
|
803
|
+
// Step 1: Create the object (metadata only)
|
|
804
|
+
const createUrl = objectUrl.replace(/\/[^/]+$/, ''); // parent collection URL
|
|
805
|
+
const result = await createObject(client.http, client.safety, createUrl, body, 'application/xml', transport);
|
|
806
|
+
// Step 2: Write source code if provided
|
|
807
|
+
if (source) {
|
|
808
|
+
// Pre-write lint validation
|
|
809
|
+
const lintWarnings = runPreWriteLint(source, type, name, config);
|
|
810
|
+
if (lintWarnings.blocked) {
|
|
811
|
+
return textResult(`Created ${type} ${name} in package ${pkg}, but source was rejected by lint:\n${lintWarnings.result.content[0].text}`);
|
|
812
|
+
}
|
|
813
|
+
await safeUpdateSource(client.http, client.safety, objectUrl, srcUrl, source, transport);
|
|
814
|
+
cachingLayer?.invalidate(type, name);
|
|
815
|
+
const msg = `Created ${type} ${name} in package ${pkg} and wrote source code.`;
|
|
816
|
+
return lintWarnings.warnings ? textResult(`${msg}\n\n${lintWarnings.warnings}`) : textResult(msg);
|
|
817
|
+
}
|
|
616
818
|
return textResult(`Created ${type} ${name} in package ${pkg}.\n${result}`);
|
|
617
819
|
}
|
|
618
820
|
case 'edit_method': {
|
|
@@ -1033,7 +1235,7 @@ async function handleSAPContext(client, args, cachingLayer) {
|
|
|
1033
1235
|
// ─── SAPManage Handler ────────────────────────────────────────────────
|
|
1034
1236
|
/** Cached feature status — populated on first probe */
|
|
1035
1237
|
let cachedFeatures;
|
|
1036
|
-
async function handleSAPManage(client, config, args, cachingLayer) {
|
|
1238
|
+
async function handleSAPManage(client, config, args, cachingLayer, isPerUserClient) {
|
|
1037
1239
|
const action = String(args.action ?? '');
|
|
1038
1240
|
switch (action) {
|
|
1039
1241
|
case 'features': {
|
|
@@ -1063,11 +1265,30 @@ async function handleSAPManage(client, config, args, cachingLayer) {
|
|
|
1063
1265
|
featureConfig.amdp = config.featureAmdp;
|
|
1064
1266
|
featureConfig.ui5 = config.featureUi5;
|
|
1065
1267
|
featureConfig.transport = config.featureTransport;
|
|
1066
|
-
|
|
1067
|
-
|
|
1268
|
+
const probed = await probeFeatures(client.http, featureConfig, config.systemType);
|
|
1269
|
+
// In PP mode with a per-user client, auth-sensitive results (401/403 on any
|
|
1270
|
+
// feature) must not poison the global cache — another user may have different
|
|
1271
|
+
// authorizations. Return the per-user result to the caller but keep the global
|
|
1272
|
+
// cache unchanged. However, when PP is enabled but the request fell back to the
|
|
1273
|
+
// shared/default client (no JWT, missing btpConfig, or non-strict fallback), the
|
|
1274
|
+
// probe ran with the same service-account credentials as the startup probe, so
|
|
1275
|
+
// updating the cache is safe and allows a manual probe to repair a failed startup.
|
|
1276
|
+
// Apply the same auth-failure sanitization as the startup probe: in PP mode,
|
|
1277
|
+
// shared-client 401/403 on textSearch must not hide source_code from users who
|
|
1278
|
+
// might have authorization via per-user clients.
|
|
1279
|
+
if (!isPerUserClient) {
|
|
1280
|
+
if (config.ppEnabled && probed.textSearch && !probed.textSearch.available) {
|
|
1281
|
+
const reason = probed.textSearch.reason ?? '';
|
|
1282
|
+
if (reason.includes('authorization') || reason.includes('401') || reason.includes('403')) {
|
|
1283
|
+
probed.textSearch = undefined;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
cachedFeatures = probed;
|
|
1287
|
+
}
|
|
1288
|
+
return textResult(JSON.stringify(probed, null, 2));
|
|
1068
1289
|
}
|
|
1069
1290
|
default:
|
|
1070
|
-
return errorResult(`Unknown SAPManage action: ${action}. Supported: features, probe`);
|
|
1291
|
+
return errorResult(`Unknown SAPManage action: ${action}. Supported: features, probe, cache_stats`);
|
|
1071
1292
|
}
|
|
1072
1293
|
}
|
|
1073
1294
|
/** Reset cached features (for testing) */
|
|
@@ -1078,4 +1299,8 @@ export function resetCachedFeatures() {
|
|
|
1078
1299
|
export function setCachedFeatures(features) {
|
|
1079
1300
|
cachedFeatures = features;
|
|
1080
1301
|
}
|
|
1302
|
+
/** Get cached features (for tool definition adaptation) */
|
|
1303
|
+
export function getCachedFeatures() {
|
|
1304
|
+
return cachedFeatures;
|
|
1305
|
+
}
|
|
1081
1306
|
//# sourceMappingURL=intent.js.map
|