abapgit-agent 1.13.1 → 1.13.2

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 CHANGED
@@ -115,7 +115,6 @@ npm run pull -- --url <git-url> --branch main
115
115
  | Installation & Setup | [INSTALL.md](INSTALL.md) |
116
116
  | All Commands Overview | [docs/commands.md](docs/commands.md) |
117
117
  | REST API Reference | [API.md](API.md) |
118
- | Error Handling | [ERROR_HANDLING.md](ERROR_HANDLING.md) |
119
118
 
120
119
  ## Dependent Projects
121
120
 
package/bin/abapgit-agent CHANGED
@@ -34,32 +34,7 @@ async function main() {
34
34
  const args = process.argv.slice(2);
35
35
  const command = args[0];
36
36
 
37
- // Load command modules (Phase 2 refactoring)
38
- const commandModules = {
39
- help: require('../src/commands/help'),
40
- health: require('../src/commands/health'),
41
- status: require('../src/commands/status'),
42
- create: require('../src/commands/create'),
43
- delete: require('../src/commands/delete'),
44
- import: require('../src/commands/import'),
45
- inspect: require('../src/commands/inspect'),
46
- unit: require('../src/commands/unit'),
47
- syntax: require('../src/commands/syntax'),
48
- tree: require('../src/commands/tree'),
49
- list: require('../src/commands/list'),
50
- view: require('../src/commands/view'),
51
- preview: require('../src/commands/preview'),
52
- where: require('../src/commands/where'),
53
- dump: require('../src/commands/dump'),
54
- debug: require('../src/commands/debug'),
55
- run: require('../src/commands/run'),
56
- ref: require('../src/commands/ref'),
57
- guide: require('../src/commands/guide'),
58
- init: require('../src/commands/init'),
59
- pull: require('../src/commands/pull'),
60
- upgrade: require('../src/commands/upgrade'),
61
- transport: require('../src/commands/transport')
62
- };
37
+ const commandModules = require('../src/commands/index');
63
38
 
64
39
  // Check if this is a modular command
65
40
  if (commandModules[command] || command === '--help' || command === '-h') {
package/package.json CHANGED
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "name": "abapgit-agent",
3
- "version": "1.13.1",
3
+ "version": "1.13.2",
4
4
  "description": "ABAP Git Agent - Pull and activate ABAP code via abapGit from any git repository",
5
- "main": "src/index.js",
6
5
  "files": [
7
6
  "bin/",
8
7
  "src/",
@@ -18,8 +17,6 @@
18
17
  "abgagt": "bin/abgagt"
19
18
  },
20
19
  "scripts": {
21
- "start": "node src/server.js",
22
- "dev": "nodemon src/server.js",
23
20
  "test": "jest",
24
21
  "test:all": "node tests/run-all.js",
25
22
  "test:unit": "jest",
@@ -51,16 +48,13 @@
51
48
  "unrelease": "node scripts/unrelease.js"
52
49
  },
53
50
  "dependencies": {
54
- "cors": "^2.8.5",
55
51
  "dotenv": "^16.3.1",
56
- "express": "^4.18.2",
57
52
  "node-fetch": "^2.7.0",
58
53
  "uuid": "^9.0.0",
59
54
  "winston": "^3.11.0"
60
55
  },
61
56
  "devDependencies": {
62
57
  "jest": "^29.7.0",
63
- "nodemon": "^3.0.1",
64
58
  "supertest": "^7.2.2"
65
59
  },
66
60
  "engines": {
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * abapgit-agent command registry
5
+ *
6
+ * Each entry maps a CLI command name to its command module.
7
+ * Command modules must export: { name, description, requiresAbapConfig, execute }
8
+ */
9
+ module.exports = {
10
+ help: require('./help'),
11
+ health: require('./health'),
12
+ status: require('./status'),
13
+ create: require('./create'),
14
+ delete: require('./delete'),
15
+ import: require('./import'),
16
+ inspect: require('./inspect'),
17
+ unit: require('./unit'),
18
+ syntax: require('./syntax'),
19
+ tree: require('./tree'),
20
+ list: require('./list'),
21
+ view: require('./view'),
22
+ preview: require('./preview'),
23
+ where: require('./where'),
24
+ dump: require('./dump'),
25
+ debug: require('./debug'),
26
+ run: require('./run'),
27
+ ref: require('./ref'),
28
+ guide: require('./guide'),
29
+ init: require('./init'),
30
+ pull: require('./pull'),
31
+ upgrade: require('./upgrade'),
32
+ transport: require('./transport')
33
+ };
@@ -6,11 +6,38 @@ const { printHttpError } = require('../utils/format-error');
6
6
  const fs = require('fs');
7
7
  const pathModule = require('path');
8
8
 
9
+ // Calculate display width accounting for emoji (2 cells) vs ASCII (1 cell)
10
+ function calcWidth(str) {
11
+ if (!str) return 0;
12
+ let width = 0;
13
+ let i = 0;
14
+ while (i < str.length) {
15
+ const code = str.codePointAt(i);
16
+ if (!code) break;
17
+ // Variation selectors (FE00-FE0F) and ZWJ (200D) take 0 width
18
+ if (code >= 0xFE00 && code <= 0xFE0F) { i += 1; continue; }
19
+ if (code === 0x200D) { i += 1; continue; } // Zero width joiner
20
+ // Emoji and wide characters take 2 cells
21
+ if (code > 0xFFFF) { width += 2; i += 2; } // Skip surrogate pair
22
+ else if (code > 127) { width += 2; i += 1; }
23
+ else { width += 1; i += 1; }
24
+ }
25
+ return width;
26
+ }
27
+
28
+ // Pad string to display width
29
+ function padToWidth(str, width) {
30
+ const s = str || '';
31
+ const currentWidth = calcWidth(s);
32
+ return s + ' '.repeat(Math.max(0, width - currentWidth));
33
+ }
9
34
  module.exports = {
10
35
  name: 'pull',
11
36
  description: 'Pull and activate ABAP objects from git repository',
12
37
  requiresAbapConfig: true,
13
38
  requiresVersionCheck: true,
39
+ _calcWidth: calcWidth,
40
+ _padToWidth: padToWidth,
14
41
 
15
42
  async execute(args, context) {
16
43
  const { loadConfig, AbapHttp, gitUtils, getTransport, getSafeguards, getConflictSettings, getTransportSettings } = context;
@@ -281,44 +308,6 @@ module.exports = {
281
308
  return icons[type] || '';
282
309
  };
283
310
 
284
- // Calculate display width accounting for emoji (2 cells) vs ASCII (1 cell)
285
- const calcWidth = (str) => {
286
- if (!str) return 0;
287
- let width = 0;
288
- let i = 0;
289
- while (i < str.length) {
290
- const code = str.codePointAt(i);
291
- if (!code) break;
292
- // Variation selectors (FE00-FE0F) and ZWJ (200D) take 0 width
293
- if (code >= 0xFE00 && code <= 0xFE0F) {
294
- i += 1;
295
- continue;
296
- }
297
- if (code === 0x200D) { // Zero width joiner
298
- i += 1;
299
- continue;
300
- }
301
- // Emoji and wide characters take 2 cells
302
- if (code > 0xFFFF) {
303
- width += 2;
304
- i += 2; // Skip surrogate pair
305
- } else if (code > 127) {
306
- width += 2;
307
- i += 1;
308
- } else {
309
- width += 1;
310
- i += 1;
311
- }
312
- }
313
- return width;
314
- };
315
-
316
- // Pad string to display width
317
- const padToWidth = (str, width) => {
318
- const s = str || '';
319
- const currentWidth = calcWidth(s);
320
- return s + ' '.repeat(Math.max(0, width - currentWidth));
321
- };
322
311
 
323
312
  if (success === 'X' || success === true) {
324
313
  console.log(`āœ… Pull completed successfully!`);
@@ -167,6 +167,8 @@ module.exports = {
167
167
  description: 'View ABAP object definitions from ABAP system',
168
168
  requiresAbapConfig: true,
169
169
  requiresVersionCheck: true,
170
+ _buildMethodLineMap: buildMethodLineMap,
171
+ _findFirstExecutableLine: findFirstExecutableLine,
170
172
 
171
173
  async execute(args, context) {
172
174
  const { loadConfig, AbapHttp } = context;
package/src/agent.js DELETED
@@ -1,512 +0,0 @@
1
- /**
2
- * ABAP Git Agent - Main agent class
3
- * Uses AbapHttp for all ABAP communication
4
- */
5
-
6
- const { AbapHttp } = require('./utils/abap-http');
7
- const { getAbapConfig } = require('./config');
8
- const logger = require('./logger');
9
-
10
- class ABAPGitAgent {
11
- constructor() {
12
- this.config = getAbapConfig();
13
- this.http = new AbapHttp(this.config);
14
- }
15
-
16
- /**
17
- * Pull repository and activate objects
18
- * @param {string} repoUrl - Git repository URL
19
- * @param {string} branch - Branch name (default: main)
20
- * @param {string} username - Git username (optional)
21
- * @param {string} password - Git password/token (optional)
22
- * @param {Array} files - Specific files to pull (optional)
23
- * @param {string} transportRequest - Transport request number (optional)
24
- * @returns {object} Pull result with success, job_id, message, error_detail
25
- */
26
- async pull(repoUrl, branch = 'main', username = null, password = null, files = null, transportRequest = null) {
27
- logger.info('Starting pull operation', { repoUrl, branch, username: !!username, files, transportRequest });
28
-
29
- try {
30
- const csrfToken = await this.http.fetchCsrfToken();
31
-
32
- const data = {
33
- url: repoUrl,
34
- branch: branch,
35
- username: username || this.config.gitUsername,
36
- password: password || this.config.gitPassword
37
- };
38
-
39
- if (files && files.length > 0) {
40
- data.files = files;
41
- }
42
-
43
- if (transportRequest) {
44
- data.transport_request = transportRequest;
45
- }
46
-
47
- const result = await this.http.post('/sap/bc/z_abapgit_agent/pull', data, { csrfToken });
48
-
49
- // Return the result directly from ABAP (handle uppercase keys from /UI2/CL_JSON)
50
- return {
51
- success: result.SUCCESS === 'X' || result.success === 'X' || result.success === true,
52
- job_id: result.JOB_ID || result.job_id,
53
- message: result.MESSAGE || result.message,
54
- error_detail: result.ERROR_DETAIL || result.error_detail || null,
55
- activated_count: result.ACTIVATED_COUNT || result.activated_count || 0,
56
- failed_count: result.FAILED_COUNT || result.failed_count || 0,
57
- activated_objects: result.ACTIVATED_OBJECTS || result.activated_objects || [],
58
- failed_objects: result.FAILED_OBJECTS || result.failed_objects || []
59
- };
60
-
61
- } catch (error) {
62
- logger.error('Pull failed', { error: error.message });
63
- throw new Error(`Pull failed: ${error.message}`);
64
- }
65
- }
66
-
67
- /**
68
- * Health check
69
- * @returns {object} Health status
70
- */
71
- async healthCheck() {
72
- try {
73
- const result = await this.http.get('/sap/bc/z_abapgit_agent/health');
74
- return {
75
- status: 'healthy',
76
- abap: 'connected',
77
- version: result.version || '1.4.0'
78
- };
79
- } catch (error) {
80
- return {
81
- status: 'unhealthy',
82
- abap: 'disconnected',
83
- error: error.message
84
- };
85
- }
86
- }
87
-
88
- /**
89
- * Check syntax of an ABAP object (legacy - not used by CLI)
90
- * @param {string} objectType - ABAP object type (e.g., 'CLAS', 'PROG', 'INTF')
91
- * @param {string} objectName - ABAP object name
92
- * @returns {object} Syntax check result with errors (if any)
93
- */
94
- async syntaxCheck(objectType, objectName) {
95
- logger.info('Starting syntax check', { objectType, objectName });
96
-
97
- try {
98
- const csrfToken = await this.http.fetchCsrfToken();
99
- const data = {
100
- object_type: objectType,
101
- object_name: objectName
102
- };
103
-
104
- const result = await this.http.post('/sap/bc/z_abapgit_agent/syntax-check', data, { csrfToken });
105
- return {
106
- success: result.SUCCESS === 'X' || result.success === 'X' || result.success === true,
107
- object_type: result.OBJECT_TYPE || result.object_type,
108
- object_name: result.OBJECT_NAME || result.object_name,
109
- error_count: result.ERROR_COUNT || result.error_count || 0,
110
- errors: result.ERRORS || result.errors || []
111
- };
112
- } catch (error) {
113
- logger.error('Syntax check failed', { error: error.message });
114
- throw new Error(`Syntax check failed: ${error.message}`);
115
- }
116
- }
117
-
118
- /**
119
- * Check syntax of ABAP source code directly (without pull/activation)
120
- * Supported types: CLAS, INTF, PROG
121
- * @param {Array} objects - Array of {type, name, source, locals_def?, locals_imp?}
122
- * @param {string} uccheck - Unicode check mode ('X' for Standard, '5' for Cloud)
123
- * @returns {object} Syntax check results with success, results array
124
- */
125
- async syntaxCheckSource(objects, uccheck = 'X') {
126
- logger.info('Starting source syntax check', { objectCount: objects.length, uccheck });
127
-
128
- try {
129
- const csrfToken = await this.http.fetchCsrfToken();
130
- const data = {
131
- objects: objects,
132
- uccheck: uccheck
133
- };
134
-
135
- const result = await this.http.post('/sap/bc/z_abapgit_agent/syntax', data, { csrfToken });
136
- const success = result.SUCCESS === 'X' || result.SUCCESS === true ||
137
- result.success === 'X' || result.success === true;
138
- const results = result.RESULTS || result.results || [];
139
- const message = result.MESSAGE || result.message || '';
140
-
141
- // Normalize results
142
- const normalizedResults = results.map(r => ({
143
- object_type: r.OBJECT_TYPE || r.object_type,
144
- object_name: r.OBJECT_NAME || r.object_name,
145
- success: r.SUCCESS === 'X' || r.SUCCESS === true || r.success === 'X' || r.success === true,
146
- error_count: r.ERROR_COUNT || r.error_count || 0,
147
- errors: r.ERRORS || r.errors || [],
148
- warnings: r.WARNINGS || r.warnings || [],
149
- message: r.MESSAGE || r.message || ''
150
- }));
151
-
152
- return {
153
- success,
154
- message,
155
- results: normalizedResults
156
- };
157
- } catch (error) {
158
- logger.error('Source syntax check failed', { error: error.message });
159
- throw new Error(`Source syntax check failed: ${error.message}`);
160
- }
161
- }
162
-
163
- /**
164
- * Run unit tests for package or objects
165
- * @param {string} packageName - Package name to run tests for (optional)
166
- * @param {Array} objects - Array of {object_type, object_name} objects (optional)
167
- * @returns {object} Unit test results with test_count, passed_count, failed_count, results
168
- */
169
- async unitCheck(packageName = null, objects = []) {
170
- logger.info('Starting unit tests', { package: packageName, objects });
171
-
172
- try {
173
- const csrfToken = await this.http.fetchCsrfToken();
174
- const data = {};
175
-
176
- if (packageName) {
177
- data.package = packageName;
178
- }
179
-
180
- if (objects && objects.length > 0) {
181
- data.objects = objects;
182
- }
183
-
184
- const result = await this.http.post('/sap/bc/z_abapgit_agent/unit', data, { csrfToken });
185
- return {
186
- success: result.SUCCESS === 'X' || result.success === 'X' || result.success === true,
187
- test_count: result.TEST_COUNT || result.test_count || 0,
188
- passed_count: result.PASSED_COUNT || result.passed_count || 0,
189
- failed_count: result.FAILED_COUNT || result.failed_count || 0,
190
- message: result.MESSAGE || result.message || '',
191
- errors: result.ERRORS || result.errors || []
192
- };
193
- } catch (error) {
194
- logger.error('Unit tests failed', { error: error.message });
195
- throw new Error(`Unit tests failed: ${error.message}`);
196
- }
197
- }
198
-
199
- /**
200
- * Create a new online repository
201
- * @param {string} repoUrl - Git repository URL
202
- * @param {string} packageName - ABAP package name
203
- * @param {string} branch - Branch name (default: 'main')
204
- * @param {string} displayName - Display name for the repository (optional)
205
- * @param {string} name - Repository name (optional)
206
- * @param {string} folderLogic - Folder logic: 'PREFIX' or 'FULL' (default: 'PREFIX')
207
- * @returns {object} Create result with success, repo_key, repo_name, message
208
- */
209
- async create(repoUrl, packageName, branch = 'main', displayName = null, name = null, folderLogic = 'PREFIX') {
210
- logger.info('Creating repository', { repoUrl, packageName, branch });
211
-
212
- try {
213
- const csrfToken = await this.http.fetchCsrfToken();
214
- const data = {
215
- url: repoUrl,
216
- package: packageName,
217
- branch: branch
218
- };
219
-
220
- if (displayName) {
221
- data.display_name = displayName;
222
- }
223
-
224
- if (name) {
225
- data.name = name;
226
- }
227
-
228
- if (folderLogic) {
229
- data.folder_logic = folderLogic;
230
- }
231
-
232
- const result = await this.http.post('/sap/bc/z_abapgit_agent/create', data, { csrfToken });
233
- return {
234
- success: result.SUCCESS === 'X' || result.success === 'X' || result.success === true,
235
- repo_key: result.REPO_KEY || result.repo_key,
236
- repo_name: result.REPO_NAME || result.repo_name,
237
- message: result.MESSAGE || result.message || '',
238
- error: result.ERROR || result.error || null
239
- };
240
- } catch (error) {
241
- logger.error('Create failed', { error: error.message });
242
- throw new Error(`Create failed: ${error.message}`);
243
- }
244
- }
245
-
246
- /**
247
- * Import existing objects from package to git repository
248
- * @param {string} repoUrl - Git repository URL
249
- * @param {string} message - Commit message (optional)
250
- * @returns {object} Import result with success, files_staged, commit_sha
251
- */
252
- async import(repoUrl, message = null) {
253
- logger.info('Starting import operation', { repoUrl, message });
254
-
255
- try {
256
- const csrfToken = await this.http.fetchCsrfToken();
257
- const data = {
258
- url: repoUrl
259
- };
260
-
261
- if (message) {
262
- data.message = message;
263
- }
264
-
265
- const result = await this.http.post('/sap/bc/z_abapgit_agent/import', data, { csrfToken });
266
- return {
267
- success: result.SUCCESS === 'X' || result.success === 'X' || result.success === true,
268
- files_staged: result.FILES_STAGED || result.files_staged || 0,
269
- commit_sha: result.COMMIT_SHA || result.commit_sha || '',
270
- message: result.MESSAGE || result.message || '',
271
- error: result.ERROR || result.error || null
272
- };
273
- } catch (error) {
274
- logger.error('Import failed', { error: error.message });
275
- throw new Error(`Import failed: ${error.message}`);
276
- }
277
- }
278
-
279
- /**
280
- * Get package hierarchy tree
281
- * @param {string} packageName - ABAP package name
282
- * @param {number} depth - Maximum depth to traverse (default: 3)
283
- * @param {boolean} includeObjects - Include object counts breakdown
284
- * @returns {object} Tree result with hierarchy, summary, and metadata
285
- */
286
- async tree(packageName, depth = 3, includeObjects = false) {
287
- logger.info('Getting package tree', { package: packageName, depth, includeObjects });
288
-
289
- try {
290
- const csrfToken = await this.http.fetchCsrfToken();
291
- const data = {
292
- package: packageName,
293
- depth: Math.min(Math.max(1, depth), 10),
294
- include_objects: includeObjects
295
- };
296
-
297
- const result = await this.http.post('/sap/bc/z_abapgit_agent/tree', data, { csrfToken });
298
- return {
299
- success: result.SUCCESS === 'X' || result.success === 'X' || result.success === true,
300
- command: result.COMMAND || result.command || 'TREE',
301
- package: result.PACKAGE || result.package,
302
- message: result.MESSAGE || result.message || '',
303
- hierarchy: result.HIERARCHY || result.hierarchy || null,
304
- summary: result.SUMMARY || result.summary || null,
305
- error: result.ERROR || result.error || null
306
- };
307
- } catch (error) {
308
- logger.error('Tree command failed', { error: error.message });
309
- throw new Error(`Tree command failed: ${error.message}`);
310
- }
311
- }
312
-
313
- /**
314
- * Preview data from ABAP tables or CDS views
315
- * @param {Array} objects - Array of table/view names
316
- * @param {string} type - Object type (TABL, DDLS, etc.)
317
- * @param {number} limit - Maximum rows to return
318
- * @param {number} offset - Number of rows to skip
319
- * @param {string} where - WHERE clause filter
320
- * @param {Array} columns - Array of column names to display
321
- * @returns {object} Preview result with rows, fields, and metadata
322
- */
323
- async preview(objects, type = null, limit = 100, offset = 0, where = null, columns = null) {
324
- logger.info('Previewing data', { objects, type, limit, offset, where, columns });
325
-
326
- try {
327
- const csrfToken = await this.http.fetchCsrfToken();
328
- const data = {
329
- objects: objects,
330
- limit: Math.min(Math.max(1, limit), 500),
331
- offset: Math.max(0, offset)
332
- };
333
-
334
- if (type) {
335
- data.type = type;
336
- }
337
-
338
- if (where) {
339
- // Convert ISO date format (YYYY-MM-DD) to ABAP DATS format (YYYYMMDD)
340
- data.where = this.convertDatesInWhereClause(where);
341
- }
342
-
343
- if (columns) {
344
- data.columns = columns;
345
- }
346
-
347
- const result = await this.http.post('/sap/bc/z_abapgit_agent/preview', data, { csrfToken });
348
- return {
349
- success: result.SUCCESS === 'X' || result.success === 'X' || result.success === true,
350
- command: result.COMMAND || result.command || 'PREVIEW',
351
- message: result.MESSAGE || result.message || '',
352
- objects: result.OBJECTS || result.objects || [],
353
- summary: result.SUMMARY || result.summary || null,
354
- pagination: result.PAGINATION || result.pagination || null,
355
- error: result.ERROR || result.error || null
356
- };
357
- } catch (error) {
358
- logger.error('Preview command failed', { error: error.message });
359
- throw new Error(`Preview command failed: ${error.message}`);
360
- }
361
- }
362
-
363
- /**
364
- * Convert ISO date formats to ABAP DATS format in WHERE clause
365
- * @param {string} whereClause - SQL WHERE clause
366
- * @returns {string} - WHERE clause with dates converted to YYYYMMDD format
367
- */
368
- convertDatesInWhereClause(whereClause) {
369
- if (!whereClause) return whereClause;
370
-
371
- const isoDatePattern = /'\\d{4}-\\d{2}-\\d{2}'/g;
372
- return whereClause.replace(isoDatePattern, (match) => {
373
- const dateContent = match.slice(1, -1);
374
- const [year, month, day] = dateContent.split('-');
375
- return `'${year}${month}${day}'`;
376
- });
377
- }
378
-
379
- /**
380
- * List objects in an ABAP package
381
- * @param {string} packageName - ABAP package name
382
- * @param {string} type - Comma-separated object types to filter (optional)
383
- * @param {string} name - Name pattern to filter (optional)
384
- * @param {number} limit - Maximum number of objects to return (default: 100)
385
- * @param {number} offset - Offset for pagination (default: 0)
386
- * @returns {object} List result with objects, by_type, total, and error
387
- */
388
- async list(packageName, type = null, name = null, limit = 100, offset = 0) {
389
- logger.info('Listing objects', { package: packageName, type, name, limit, offset });
390
-
391
- try {
392
- const csrfToken = await this.http.fetchCsrfToken();
393
- const data = {
394
- package: packageName,
395
- limit: Math.min(Math.max(1, limit), 1000),
396
- offset: Math.max(0, offset)
397
- };
398
-
399
- if (type) {
400
- data.type = type;
401
- }
402
-
403
- if (name) {
404
- data.name = name;
405
- }
406
-
407
- const result = await this.http.post('/sap/bc/z_abapgit_agent/list', data, { csrfToken });
408
-
409
- // Normalize objects array to use lowercase keys
410
- const rawObjects = result.OBJECTS || result.objects || [];
411
- const normalizedObjects = rawObjects.map(obj => ({
412
- type: obj.TYPE || obj.type,
413
- name: obj.NAME || obj.name
414
- }));
415
-
416
- // Normalize by_type array to use lowercase keys
417
- const rawByType = result.BY_TYPE || result.by_type || [];
418
- const normalizedByType = rawByType.map(item => ({
419
- type: item.TYPE || item.type,
420
- count: item.COUNT || item.count || 0
421
- }));
422
-
423
- return {
424
- success: result.SUCCESS === 'X' || result.success === 'X' || result.success === true,
425
- command: result.COMMAND || result.command || 'LIST',
426
- package: result.PACKAGE || result.package || packageName,
427
- message: result.MESSAGE || result.message || '',
428
- objects: normalizedObjects,
429
- by_type: normalizedByType,
430
- total: result.TOTAL || result.total || 0,
431
- error: result.ERROR || result.error || null
432
- };
433
- } catch (error) {
434
- logger.error('List command failed', { error: error.message });
435
- throw new Error(`List command failed: ${error.message}`);
436
- }
437
- }
438
-
439
- /**
440
- * View ABAP object definitions
441
- * @param {Array} objects - Array of object names to view
442
- * @param {string} type - Object type (optional, e.g., 'CLAS', 'TABL')
443
- * @returns {object} View result with object definitions
444
- */
445
- async view(objects, type = null) {
446
- logger.info('Viewing objects', { objects, type });
447
-
448
- try {
449
- const csrfToken = await this.http.fetchCsrfToken();
450
- const data = {
451
- objects: objects
452
- };
453
-
454
- if (type) {
455
- data.type = type;
456
- }
457
-
458
- const result = await this.http.post('/sap/bc/z_abapgit_agent/view', data, { csrfToken });
459
- return {
460
- success: result.SUCCESS === 'X' || result.success === 'X' || result.success === true,
461
- command: result.COMMAND || result.command || 'VIEW',
462
- objects: result.OBJECTS || result.objects || [],
463
- error: result.ERROR || result.error || null
464
- };
465
- } catch (error) {
466
- logger.error('View command failed', { error: error.message });
467
- throw new Error(`View command failed: ${error.message}`);
468
- }
469
- }
470
-
471
- /**
472
- * Find where-used list for ABAP objects
473
- * @param {Array} objects - Array of object names to search
474
- * @param {string} type - Object type (optional)
475
- * @param {number} limit - Maximum results (default: 100, max: 500)
476
- * @param {number} offset - Number of results to skip (default: 0)
477
- * @returns {object} Where-used result with found objects
478
- */
479
- async where(objects, type = null, limit = 100, offset = 0) {
480
- logger.info('Finding where-used', { objects, type, limit, offset });
481
-
482
- try {
483
- const csrfToken = await this.http.fetchCsrfToken();
484
- const data = {
485
- objects: objects,
486
- limit: Math.min(Math.max(1, limit), 500),
487
- offset: Math.max(0, offset)
488
- };
489
-
490
- if (type) {
491
- data.type = type;
492
- }
493
-
494
- const result = await this.http.post('/sap/bc/z_abapgit_agent/where-used', data, { csrfToken });
495
- return {
496
- success: result.SUCCESS === 'X' || result.success === 'X' || result.success === true,
497
- command: result.COMMAND || result.command || 'WHERE',
498
- objects: result.OBJECTS || result.objects || [],
499
- message: result.MESSAGE || result.message || '',
500
- pagination: result.PAGINATION || result.pagination || null,
501
- error: result.ERROR || result.error || null
502
- };
503
- } catch (error) {
504
- logger.error('Where command failed', { error: error.message });
505
- throw new Error(`Where command failed: ${error.message}`);
506
- }
507
- }
508
- }
509
-
510
- module.exports = {
511
- ABAPGitAgent
512
- };
package/src/index.js DELETED
@@ -1,48 +0,0 @@
1
- /**
2
- * ABAP Git Agent - Package Entry Point
3
- *
4
- * Exports functions for programmatic use:
5
- * const { pull, healthCheck } = require('abapgit-agent');
6
- */
7
-
8
- const { ABAPGitAgent } = require('./agent');
9
-
10
- /**
11
- * Pull repository and activate objects
12
- * @param {string} repoUrl - Git repository URL
13
- * @param {string} branch - Branch name (default: main)
14
- * @param {string} username - Git username (optional)
15
- * @param {string} password - Git password/token (optional)
16
- * @returns {object} Pull result with success, job_id, message, error_detail
17
- */
18
- async function pull(repoUrl, branch = 'main', username = null, password = null) {
19
- const agent = new ABAPGitAgent();
20
- return await agent.pull(repoUrl, branch, username, password);
21
- }
22
-
23
- /**
24
- * Check agent health
25
- * @returns {object} Health status
26
- */
27
- async function healthCheck() {
28
- const agent = new ABAPGitAgent();
29
- return await agent.healthCheck();
30
- }
31
-
32
- /**
33
- * Check if integration is configured for current directory
34
- * @returns {boolean} True if .abapGitAgent exists
35
- */
36
- function isConfigured() {
37
- const fs = require('fs');
38
- const path = require('path');
39
- const repoConfigPath = path.join(process.cwd(), '.abapGitAgent');
40
- return fs.existsSync(repoConfigPath);
41
- }
42
-
43
- module.exports = {
44
- pull,
45
- healthCheck,
46
- isConfigured,
47
- ABAPGitAgent
48
- };
package/src/server.js DELETED
@@ -1,116 +0,0 @@
1
- /**
2
- * HTTP Server for Claude Integration
3
- */
4
-
5
- const express = require('express');
6
- const cors = require('cors');
7
- const { ABAPGitAgent } = require('./agent');
8
- const { getAgentConfig } = require('./config');
9
- const logger = require('./logger');
10
-
11
- class Server {
12
- constructor() {
13
- this.app = express();
14
- this.agent = new ABAPGitAgent();
15
- this.agentConfig = getAgentConfig();
16
-
17
- this.setupMiddleware();
18
- this.setupRoutes();
19
- }
20
-
21
- setupMiddleware() {
22
- this.app.use(cors());
23
- this.app.use(express.json());
24
-
25
- // Request logging
26
- this.app.use((req, res, next) => {
27
- logger.debug(`${req.method} ${req.path}`, { body: req.body });
28
- next();
29
- });
30
-
31
- // Error handling
32
- this.app.use((err, req, res, next) => {
33
- logger.error('Request error', { error: err.message, stack: err.stack });
34
- res.status(500).json({
35
- success: false,
36
- error: err.message
37
- });
38
- });
39
- }
40
-
41
- setupRoutes() {
42
- // Health check
43
- this.app.get('/api/health', async (req, res) => {
44
- try {
45
- const health = await this.agent.healthCheck();
46
- res.json(health);
47
- } catch (error) {
48
- res.status(503).json({
49
- status: 'unhealthy',
50
- error: error.message
51
- });
52
- }
53
- });
54
-
55
- // Pull repository (synchronous - returns immediately with result)
56
- this.app.post('/api/pull', async (req, res) => {
57
- try {
58
- const { url, branch, username, password } = req.body;
59
-
60
- if (!url) {
61
- return res.status(400).json({
62
- success: false,
63
- error: 'Missing required parameter: url'
64
- });
65
- }
66
-
67
- const result = await this.agent.pull(url, branch, username, password);
68
- res.json(result);
69
-
70
- } catch (error) {
71
- logger.error('Pull failed', { error: error.message });
72
- res.status(500).json({
73
- success: false,
74
- error: error.message
75
- });
76
- }
77
- });
78
- }
79
-
80
- start() {
81
- const port = this.agentConfig.port || 3000;
82
-
83
- this.server = this.app.listen(port, () => {
84
- logger.info(`ABAP AI Bridge server started on port ${port}`);
85
- console.log(`\nšŸš€ ABAP AI Bridge is running!`);
86
- console.log(` Health: http://localhost:${port}/api/health`);
87
- console.log(` Pull: POST http://localhost:${port}/api/pull`);
88
- console.log(`\nšŸ“š API Documentation:`);
89
- console.log(` POST /api/pull { "url": "git-url", "branch": "main" }`);
90
- });
91
-
92
- // Graceful shutdown
93
- process.on('SIGTERM', () => this.shutdown());
94
- process.on('SIGINT', () => this.shutdown());
95
- }
96
-
97
- shutdown() {
98
- logger.info('Shutting down server...');
99
- if (this.server) {
100
- this.server.close(() => {
101
- logger.info('Server closed');
102
- process.exit(0);
103
- });
104
- }
105
- }
106
- }
107
-
108
- // Start server if run directly
109
- if (require.main === module) {
110
- const server = new Server();
111
- server.start();
112
- }
113
-
114
- module.exports = {
115
- Server
116
- };