@mcp-abap-adt/connection 0.1.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/LICENSE +21 -0
- package/README.md +80 -0
- package/bin/sap-abap-auth.js +600 -0
- package/dist/config/sapConfig.d.ts +43 -0
- package/dist/config/sapConfig.d.ts.map +1 -0
- package/dist/config/sapConfig.js +202 -0
- package/dist/connection/AbapConnection.d.ts +22 -0
- package/dist/connection/AbapConnection.d.ts.map +1 -0
- package/dist/connection/AbapConnection.js +2 -0
- package/dist/connection/AbstractAbapConnection.d.ts +115 -0
- package/dist/connection/AbstractAbapConnection.d.ts.map +1 -0
- package/dist/connection/AbstractAbapConnection.js +716 -0
- package/dist/connection/BaseAbapConnection.d.ts +17 -0
- package/dist/connection/BaseAbapConnection.d.ts.map +1 -0
- package/dist/connection/BaseAbapConnection.js +68 -0
- package/dist/connection/JwtAbapConnection.d.ts +33 -0
- package/dist/connection/JwtAbapConnection.d.ts.map +1 -0
- package/dist/connection/JwtAbapConnection.js +305 -0
- package/dist/connection/connectionFactory.d.ts +5 -0
- package/dist/connection/connectionFactory.d.ts.map +1 -0
- package/dist/connection/connectionFactory.js +15 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/logger.d.ts +67 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +2 -0
- package/dist/utils/FileSessionStorage.d.ts +73 -0
- package/dist/utils/FileSessionStorage.d.ts.map +1 -0
- package/dist/utils/FileSessionStorage.js +191 -0
- package/dist/utils/timeouts.d.ts +8 -0
- package/dist/utils/timeouts.d.ts.map +1 -0
- package/dist/utils/timeouts.js +21 -0
- package/dist/utils/tokenRefresh.d.ts +17 -0
- package/dist/utils/tokenRefresh.d.ts.map +1 -0
- package/dist/utils/tokenRefresh.js +53 -0
- package/package.json +63 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based session storage implementation
|
|
3
|
+
* Stores session state (cookies, CSRF tokens) in JSON files on disk
|
|
4
|
+
*/
|
|
5
|
+
import { ISessionStorage, SessionState } from '../logger.js';
|
|
6
|
+
export interface FileSessionStorageOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Directory to store session files
|
|
9
|
+
* @default '.sessions'
|
|
10
|
+
*/
|
|
11
|
+
sessionDir?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Whether to create session directory if it doesn't exist
|
|
14
|
+
* @default true
|
|
15
|
+
*/
|
|
16
|
+
createDir?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Pretty-print JSON files (for debugging)
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
prettyPrint?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* File-based session storage
|
|
25
|
+
* Stores each session in a separate JSON file: .sessions/<sessionId>.json
|
|
26
|
+
*/
|
|
27
|
+
export declare class FileSessionStorage implements ISessionStorage {
|
|
28
|
+
private readonly sessionDir;
|
|
29
|
+
private readonly prettyPrint;
|
|
30
|
+
constructor(options?: FileSessionStorageOptions);
|
|
31
|
+
/**
|
|
32
|
+
* Get file path for session
|
|
33
|
+
*/
|
|
34
|
+
private getSessionFilePath;
|
|
35
|
+
/**
|
|
36
|
+
* Save session state to file
|
|
37
|
+
*/
|
|
38
|
+
save(sessionId: string, state: SessionState): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Load session state from file
|
|
41
|
+
*/
|
|
42
|
+
load(sessionId: string): Promise<SessionState | null>;
|
|
43
|
+
/**
|
|
44
|
+
* Delete session state file
|
|
45
|
+
*/
|
|
46
|
+
delete(sessionId: string): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* List all session IDs
|
|
49
|
+
*/
|
|
50
|
+
listSessions(): Promise<string[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Get session metadata (without loading full state)
|
|
53
|
+
*/
|
|
54
|
+
getSessionMetadata(sessionId: string): Promise<{
|
|
55
|
+
sessionId: string;
|
|
56
|
+
timestamp: number;
|
|
57
|
+
pid: number;
|
|
58
|
+
age: number;
|
|
59
|
+
} | null>;
|
|
60
|
+
/**
|
|
61
|
+
* Clean up stale sessions (older than maxAge)
|
|
62
|
+
*/
|
|
63
|
+
cleanupStaleSessions(maxAgeMs?: number): Promise<string[]>;
|
|
64
|
+
/**
|
|
65
|
+
* Clean up sessions from dead processes
|
|
66
|
+
*/
|
|
67
|
+
cleanupDeadProcessSessions(): Promise<string[]>;
|
|
68
|
+
/**
|
|
69
|
+
* Clear all sessions
|
|
70
|
+
*/
|
|
71
|
+
clearAll(): Promise<void>;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=FileSessionStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileSessionStorage.d.ts","sourceRoot":"","sources":["../../src/utils/FileSessionStorage.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE7D,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,eAAe;IACxD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;gBAE1B,OAAO,GAAE,yBAA8B;IAenD;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAM1B;;OAEG;IACG,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBjE;;OAEG;IACG,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAiB3D;;OAEG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9C;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAWvC;;OAEG;IACG,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QACnD,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;KACb,GAAG,IAAI,CAAC;IAqBT;;OAEG;IACG,oBAAoB,CAAC,QAAQ,GAAE,MAAuB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAgBhF;;OAEG;IACG,0BAA0B,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAqBrD;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAMhC"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* File-based session storage implementation
|
|
4
|
+
* Stores session state (cookies, CSRF tokens) in JSON files on disk
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.FileSessionStorage = void 0;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
/**
|
|
44
|
+
* File-based session storage
|
|
45
|
+
* Stores each session in a separate JSON file: .sessions/<sessionId>.json
|
|
46
|
+
*/
|
|
47
|
+
class FileSessionStorage {
|
|
48
|
+
sessionDir;
|
|
49
|
+
prettyPrint;
|
|
50
|
+
constructor(options = {}) {
|
|
51
|
+
const { sessionDir = '.sessions', createDir = true, prettyPrint = false } = options;
|
|
52
|
+
this.sessionDir = path.resolve(process.cwd(), sessionDir);
|
|
53
|
+
this.prettyPrint = prettyPrint;
|
|
54
|
+
if (createDir && !fs.existsSync(this.sessionDir)) {
|
|
55
|
+
fs.mkdirSync(this.sessionDir, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get file path for session
|
|
60
|
+
*/
|
|
61
|
+
getSessionFilePath(sessionId) {
|
|
62
|
+
// Sanitize session ID to prevent path traversal
|
|
63
|
+
const sanitizedId = sessionId.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
64
|
+
return path.join(this.sessionDir, `${sanitizedId}.json`);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Save session state to file
|
|
68
|
+
*/
|
|
69
|
+
async save(sessionId, state) {
|
|
70
|
+
const filePath = this.getSessionFilePath(sessionId);
|
|
71
|
+
const data = {
|
|
72
|
+
sessionId,
|
|
73
|
+
timestamp: Date.now(),
|
|
74
|
+
pid: process.pid,
|
|
75
|
+
state
|
|
76
|
+
};
|
|
77
|
+
const json = this.prettyPrint
|
|
78
|
+
? JSON.stringify(data, null, 2)
|
|
79
|
+
: JSON.stringify(data);
|
|
80
|
+
await fs.promises.writeFile(filePath, json, 'utf-8');
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Load session state from file
|
|
84
|
+
*/
|
|
85
|
+
async load(sessionId) {
|
|
86
|
+
const filePath = this.getSessionFilePath(sessionId);
|
|
87
|
+
if (!fs.existsSync(filePath)) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const json = await fs.promises.readFile(filePath, 'utf-8');
|
|
92
|
+
const data = JSON.parse(json);
|
|
93
|
+
return data.state || null;
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
// File corrupted or invalid JSON
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Delete session state file
|
|
102
|
+
*/
|
|
103
|
+
async delete(sessionId) {
|
|
104
|
+
const filePath = this.getSessionFilePath(sessionId);
|
|
105
|
+
if (fs.existsSync(filePath)) {
|
|
106
|
+
await fs.promises.unlink(filePath);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* List all session IDs
|
|
111
|
+
*/
|
|
112
|
+
async listSessions() {
|
|
113
|
+
if (!fs.existsSync(this.sessionDir)) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
const files = await fs.promises.readdir(this.sessionDir);
|
|
117
|
+
return files
|
|
118
|
+
.filter(f => f.endsWith('.json'))
|
|
119
|
+
.map(f => f.replace('.json', ''));
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get session metadata (without loading full state)
|
|
123
|
+
*/
|
|
124
|
+
async getSessionMetadata(sessionId) {
|
|
125
|
+
const filePath = this.getSessionFilePath(sessionId);
|
|
126
|
+
if (!fs.existsSync(filePath)) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const json = await fs.promises.readFile(filePath, 'utf-8');
|
|
131
|
+
const data = JSON.parse(json);
|
|
132
|
+
return {
|
|
133
|
+
sessionId: data.sessionId,
|
|
134
|
+
timestamp: data.timestamp,
|
|
135
|
+
pid: data.pid,
|
|
136
|
+
age: Date.now() - data.timestamp
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Clean up stale sessions (older than maxAge)
|
|
145
|
+
*/
|
|
146
|
+
async cleanupStaleSessions(maxAgeMs = 30 * 60 * 1000) {
|
|
147
|
+
const sessions = await this.listSessions();
|
|
148
|
+
const stale = [];
|
|
149
|
+
const now = Date.now();
|
|
150
|
+
for (const sessionId of sessions) {
|
|
151
|
+
const metadata = await this.getSessionMetadata(sessionId);
|
|
152
|
+
if (metadata && (now - metadata.timestamp) > maxAgeMs) {
|
|
153
|
+
await this.delete(sessionId);
|
|
154
|
+
stale.push(sessionId);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return stale;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Clean up sessions from dead processes
|
|
161
|
+
*/
|
|
162
|
+
async cleanupDeadProcessSessions() {
|
|
163
|
+
const sessions = await this.listSessions();
|
|
164
|
+
const dead = [];
|
|
165
|
+
for (const sessionId of sessions) {
|
|
166
|
+
const metadata = await this.getSessionMetadata(sessionId);
|
|
167
|
+
if (metadata) {
|
|
168
|
+
try {
|
|
169
|
+
// Check if process is still running
|
|
170
|
+
process.kill(metadata.pid, 0);
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
// Process doesn't exist
|
|
174
|
+
await this.delete(sessionId);
|
|
175
|
+
dead.push(sessionId);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return dead;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Clear all sessions
|
|
183
|
+
*/
|
|
184
|
+
async clearAll() {
|
|
185
|
+
const sessions = await this.listSessions();
|
|
186
|
+
for (const sessionId of sessions) {
|
|
187
|
+
await this.delete(sessionId);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
exports.FileSessionStorage = FileSessionStorage;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface TimeoutConfig {
|
|
2
|
+
default: number;
|
|
3
|
+
csrf: number;
|
|
4
|
+
long: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function getTimeoutConfig(): TimeoutConfig;
|
|
7
|
+
export declare function getTimeout(type?: "default" | "csrf" | "long" | number): number;
|
|
8
|
+
//# sourceMappingURL=timeouts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeouts.d.ts","sourceRoot":"","sources":["../../src/utils/timeouts.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,gBAAgB,IAAI,aAAa,CAUhD;AAED,wBAAgB,UAAU,CAAC,IAAI,GAAE,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,MAAkB,GAAG,MAAM,CAOzF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getTimeoutConfig = getTimeoutConfig;
|
|
4
|
+
exports.getTimeout = getTimeout;
|
|
5
|
+
function getTimeoutConfig() {
|
|
6
|
+
const defaultTimeout = parseInt(process.env.SAP_TIMEOUT_DEFAULT || "45000", 10);
|
|
7
|
+
const csrfTimeout = parseInt(process.env.SAP_TIMEOUT_CSRF || "15000", 10);
|
|
8
|
+
const longTimeout = parseInt(process.env.SAP_TIMEOUT_LONG || "60000", 10);
|
|
9
|
+
return {
|
|
10
|
+
default: defaultTimeout,
|
|
11
|
+
csrf: csrfTimeout,
|
|
12
|
+
long: longTimeout
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function getTimeout(type = "default") {
|
|
16
|
+
if (typeof type === "number") {
|
|
17
|
+
return type;
|
|
18
|
+
}
|
|
19
|
+
const config = getTimeoutConfig();
|
|
20
|
+
return config[type];
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token refresh utilities for JWT authentication
|
|
3
|
+
*/
|
|
4
|
+
export interface TokenRefreshResult {
|
|
5
|
+
accessToken: string;
|
|
6
|
+
refreshToken?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Refreshes the access token using refresh token
|
|
10
|
+
* @param refreshToken Refresh token
|
|
11
|
+
* @param uaaUrl UAA URL (e.g., https://your-account.authentication.eu10.hana.ondemand.com)
|
|
12
|
+
* @param clientId UAA client ID
|
|
13
|
+
* @param clientSecret UAA client secret
|
|
14
|
+
* @returns Promise that resolves to new tokens
|
|
15
|
+
*/
|
|
16
|
+
export declare function refreshJwtToken(refreshToken: string, uaaUrl: string, clientId: string, clientSecret: string): Promise<TokenRefreshResult>;
|
|
17
|
+
//# sourceMappingURL=tokenRefresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenRefresh.d.ts","sourceRoot":"","sources":["../../src/utils/tokenRefresh.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,kBAAkB,CAAC,CAqC7B"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Token refresh utilities for JWT authentication
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.refreshJwtToken = refreshJwtToken;
|
|
10
|
+
const axios_1 = __importDefault(require("axios"));
|
|
11
|
+
/**
|
|
12
|
+
* Refreshes the access token using refresh token
|
|
13
|
+
* @param refreshToken Refresh token
|
|
14
|
+
* @param uaaUrl UAA URL (e.g., https://your-account.authentication.eu10.hana.ondemand.com)
|
|
15
|
+
* @param clientId UAA client ID
|
|
16
|
+
* @param clientSecret UAA client secret
|
|
17
|
+
* @returns Promise that resolves to new tokens
|
|
18
|
+
*/
|
|
19
|
+
async function refreshJwtToken(refreshToken, uaaUrl, clientId, clientSecret) {
|
|
20
|
+
try {
|
|
21
|
+
const tokenUrl = `${uaaUrl}/oauth/token`;
|
|
22
|
+
const params = new URLSearchParams();
|
|
23
|
+
params.append('grant_type', 'refresh_token');
|
|
24
|
+
params.append('refresh_token', refreshToken);
|
|
25
|
+
const authString = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
|
|
26
|
+
const response = await (0, axios_1.default)({
|
|
27
|
+
method: 'post',
|
|
28
|
+
url: tokenUrl,
|
|
29
|
+
headers: {
|
|
30
|
+
Authorization: `Basic ${authString}`,
|
|
31
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
32
|
+
},
|
|
33
|
+
data: params.toString(),
|
|
34
|
+
});
|
|
35
|
+
if (response.data && response.data.access_token) {
|
|
36
|
+
return {
|
|
37
|
+
accessToken: response.data.access_token,
|
|
38
|
+
refreshToken: response.data.refresh_token || refreshToken, // Use new refresh token if provided, otherwise keep old one
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
throw new Error('Response does not contain access_token');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (error.response) {
|
|
47
|
+
throw new Error(`Token refresh failed (${error.response.status}): ${JSON.stringify(error.response.data)}`);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
throw new Error(`Token refresh failed: ${error.message}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mcp-abap-adt/connection",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "ABAP connection layer for MCP ABAP ADT server",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"bin",
|
|
10
|
+
"README.md",
|
|
11
|
+
"LICENSE"
|
|
12
|
+
],
|
|
13
|
+
"bin": {
|
|
14
|
+
"sap-abap-auth": "./bin/sap-abap-auth.js"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"abap",
|
|
18
|
+
"sap",
|
|
19
|
+
"adt",
|
|
20
|
+
"connection",
|
|
21
|
+
"mcp",
|
|
22
|
+
"abap-adt"
|
|
23
|
+
],
|
|
24
|
+
"author": "Oleksii Kyslytsia <oleksij.kyslytsja@gmail.com>",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"homepage": "https://github.com/fr0ster/mcp-abap-adt#readme",
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/fr0ster/mcp-abap-adt/issues"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/fr0ster/mcp-abap-adt.git",
|
|
33
|
+
"directory": "packages/connection"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
37
|
+
"build": "npm run clean --silent && npx tsc -p tsconfig.json",
|
|
38
|
+
"build:fast": "npx tsc -p tsconfig.json",
|
|
39
|
+
"install:local": "npm install --no-workspaces",
|
|
40
|
+
"pack": "npm run build && npm pack",
|
|
41
|
+
"test": "jest",
|
|
42
|
+
"prepublishOnly": "npm run build"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18.0.0"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"axios": "^1.11.0",
|
|
49
|
+
"commander": "^14.0.2",
|
|
50
|
+
"express": "^5.1.0",
|
|
51
|
+
"open": "^11.0.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/jest": "^30.0.0",
|
|
55
|
+
"@types/node": "^24.10.1",
|
|
56
|
+
"jest": "^30.2.0",
|
|
57
|
+
"ts-jest": "^29.2.5",
|
|
58
|
+
"typescript": "^5.9.2"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
}
|
|
63
|
+
}
|