@linkup-ai/abap-ai 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.
Files changed (119) hide show
  1. package/README.md +389 -0
  2. package/dist/adt-client.js +383 -0
  3. package/dist/cli/activate.js +127 -0
  4. package/dist/cli/init.js +559 -0
  5. package/dist/cli/link.js +148 -0
  6. package/dist/cli/remove.js +83 -0
  7. package/dist/cli/status.js +231 -0
  8. package/dist/cli/systems.js +72 -0
  9. package/dist/cli.js +92 -0
  10. package/dist/index.js +1442 -0
  11. package/dist/knowledge/abap/abap-dictionary.md +199 -0
  12. package/dist/knowledge/abap/abap-sql.md +296 -0
  13. package/dist/knowledge/abap/amdp.md +273 -0
  14. package/dist/knowledge/abap/clean-code.md +293 -0
  15. package/dist/knowledge/abap/cloud-background-processing.md +250 -0
  16. package/dist/knowledge/abap/cloud-communication.md +265 -0
  17. package/dist/knowledge/abap/cloud-development.md +176 -0
  18. package/dist/knowledge/abap/cloud-extensibility.md +252 -0
  19. package/dist/knowledge/abap/cloud-released-apis.md +261 -0
  20. package/dist/knowledge/abap/constructor-expressions.md +289 -0
  21. package/dist/knowledge/abap/enhancements.md +232 -0
  22. package/dist/knowledge/abap/exceptions.md +271 -0
  23. package/dist/knowledge/abap/internal-tables.md +205 -0
  24. package/dist/knowledge/abap/object-orientation.md +298 -0
  25. package/dist/knowledge/abap/performance.md +216 -0
  26. package/dist/knowledge/abap/rap-abstract-entities.md +206 -0
  27. package/dist/knowledge/abap/rap-business-events.md +216 -0
  28. package/dist/knowledge/abap/rap-draft.md +191 -0
  29. package/dist/knowledge/abap/rap-eml.md +453 -0
  30. package/dist/knowledge/abap/rap-end-to-end.md +486 -0
  31. package/dist/knowledge/abap/rap-feature-control.md +185 -0
  32. package/dist/knowledge/abap/rap-numbering.md +280 -0
  33. package/dist/knowledge/abap/rap-service-exposure.md +163 -0
  34. package/dist/knowledge/abap/rap-unmanaged.md +468 -0
  35. package/dist/knowledge/abap/string-processing.md +180 -0
  36. package/dist/knowledge/abap/unit-testing.md +303 -0
  37. package/dist/knowledge/abap-cds/access-control.md +241 -0
  38. package/dist/knowledge/abap-cds/annotations.md +331 -0
  39. package/dist/knowledge/abap-cds/associations.md +254 -0
  40. package/dist/knowledge/abap-cds/expressions.md +230 -0
  41. package/dist/knowledge/abap-cds/functions.md +245 -0
  42. package/dist/knowledge/abap-cds/metadata-extensions.md +294 -0
  43. package/dist/knowledge/cap/authentication.md +278 -0
  44. package/dist/knowledge/cap/cdl-syntax.md +247 -0
  45. package/dist/knowledge/cap/cql-queries.md +266 -0
  46. package/dist/knowledge/cap/deployment.md +343 -0
  47. package/dist/knowledge/cap/event-handlers.md +287 -0
  48. package/dist/knowledge/cap/fiori-integration.md +303 -0
  49. package/dist/knowledge/cap/service-definitions.md +287 -0
  50. package/dist/knowledge/fiori/annotations.md +347 -0
  51. package/dist/knowledge/fiori/deployment.md +340 -0
  52. package/dist/knowledge/fiori/fiori-elements.md +332 -0
  53. package/dist/knowledge/fiori/fiori-side-effects.md +107 -0
  54. package/dist/knowledge/fiori/fiori-valuelist.md +144 -0
  55. package/dist/knowledge/fiori/ui5-controllers.md +358 -0
  56. package/dist/knowledge/fiori/ui5-data-binding.md +311 -0
  57. package/dist/knowledge/fiori/ui5-fragments-dialogs.md +330 -0
  58. package/dist/knowledge/fiori/ui5-manifest.md +411 -0
  59. package/dist/knowledge/fiori/ui5-routing.md +303 -0
  60. package/dist/knowledge/fiori/ui5-xml-views.md +294 -0
  61. package/dist/license-guard.js +81 -0
  62. package/dist/logger.js +114 -0
  63. package/dist/postinstall.js +165 -0
  64. package/dist/security-audit.js +136 -0
  65. package/dist/security-policy.js +322 -0
  66. package/dist/system-profile.js +207 -0
  67. package/dist/tools/abap-doc.js +72 -0
  68. package/dist/tools/abapgit.js +161 -0
  69. package/dist/tools/activate.js +71 -0
  70. package/dist/tools/atc-check.js +117 -0
  71. package/dist/tools/auth-object.js +56 -0
  72. package/dist/tools/breakpoints.js +76 -0
  73. package/dist/tools/call-hierarchy.js +84 -0
  74. package/dist/tools/cds-annotations.js +98 -0
  75. package/dist/tools/cds-dependencies.js +65 -0
  76. package/dist/tools/check.js +47 -0
  77. package/dist/tools/code-completion.js +70 -0
  78. package/dist/tools/code-coverage.js +111 -0
  79. package/dist/tools/create-amdp.js +111 -0
  80. package/dist/tools/create-dcl.js +81 -0
  81. package/dist/tools/create-transport.js +38 -0
  82. package/dist/tools/create.js +285 -0
  83. package/dist/tools/data-preview.js +37 -0
  84. package/dist/tools/delete.js +45 -0
  85. package/dist/tools/deploy-bsp.js +298 -0
  86. package/dist/tools/discovery.js +59 -0
  87. package/dist/tools/element-info.js +93 -0
  88. package/dist/tools/enhancements.js +186 -0
  89. package/dist/tools/extract-method.js +44 -0
  90. package/dist/tools/function-group.js +59 -0
  91. package/dist/tools/knowledge.js +275 -0
  92. package/dist/tools/lock-object.js +75 -0
  93. package/dist/tools/message-class.js +67 -0
  94. package/dist/tools/navigate.js +80 -0
  95. package/dist/tools/number-range.js +57 -0
  96. package/dist/tools/object-documentation.js +43 -0
  97. package/dist/tools/object-structure.js +78 -0
  98. package/dist/tools/object-versions.js +57 -0
  99. package/dist/tools/package-contents.js +60 -0
  100. package/dist/tools/pretty-printer.js +35 -0
  101. package/dist/tools/publish-binding.js +49 -0
  102. package/dist/tools/quick-fix.js +69 -0
  103. package/dist/tools/read.js +172 -0
  104. package/dist/tools/refactor-rename.js +60 -0
  105. package/dist/tools/release-transport.js +24 -0
  106. package/dist/tools/released-apis.js +51 -0
  107. package/dist/tools/repository-tree.js +90 -0
  108. package/dist/tools/scaffold-rap.js +642 -0
  109. package/dist/tools/search.js +73 -0
  110. package/dist/tools/shared/data-format.js +101 -0
  111. package/dist/tools/sql-console.js +17 -0
  112. package/dist/tools/system-info.js +271 -0
  113. package/dist/tools/traces.js +66 -0
  114. package/dist/tools/transport-contents.js +83 -0
  115. package/dist/tools/transports.js +68 -0
  116. package/dist/tools/unit-test.js +135 -0
  117. package/dist/tools/where-used.js +59 -0
  118. package/dist/tools/write.js +120 -0
  119. package/package.json +50 -0
@@ -0,0 +1,383 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.http = exports.SUPPORTED_OBJECT_TYPES = exports.AdtError = exports.SAP_LANGUAGE = exports.SAP_USER = void 0;
7
+ exports.resolveAdtPath = resolveAdtPath;
8
+ exports.adtGet = adtGet;
9
+ exports.adtGetXml = adtGetXml;
10
+ exports.adtGetWithEtag = adtGetWithEtag;
11
+ exports.adtPut = adtPut;
12
+ exports.adtLock = adtLock;
13
+ exports.adtUnlock = adtUnlock;
14
+ exports.adtDelete = adtDelete;
15
+ exports.adtPostXml = adtPostXml;
16
+ exports.adtGetXmlWithParams = adtGetXmlWithParams;
17
+ exports.adtPostText = adtPostText;
18
+ exports.ensureSession = ensureSession;
19
+ exports.resetSession = resetSession;
20
+ const axios_1 = __importDefault(require("axios"));
21
+ const axios_cookiejar_support_1 = require("axios-cookiejar-support");
22
+ const tough_cookie_1 = require("tough-cookie");
23
+ const logger_js_1 = require("./logger.js");
24
+ // ─── Configuração via env ─────────────────────────────────────────
25
+ const SAP_URL = process.env.SAP_URL ?? "";
26
+ exports.SAP_USER = process.env.SAP_USER ?? "";
27
+ const SAP_PASS = process.env.SAP_PASS ?? "";
28
+ const SAP_CLIENT = process.env.SAP_CLIENT ?? "100";
29
+ exports.SAP_LANGUAGE = process.env.SAP_LANGUAGE ?? "PT";
30
+ /** Timeout em ms para chamadas ADT (padrão: 60s, configurável via SAP_TIMEOUT) */
31
+ const SAP_TIMEOUT = parseInt(process.env.SAP_TIMEOUT ?? "60000", 10);
32
+ /** Número máximo de retries em 401 (sessão expirada) */
33
+ const MAX_RETRIES = 1;
34
+ let session = null;
35
+ // ─── HTTP Client ──────────────────────────────────────────────────
36
+ function createHttpClient() {
37
+ const jar = new tough_cookie_1.CookieJar();
38
+ // NODE_TLS_REJECT_UNAUTHORIZED=0 já está definido no mcp.json para ignorar certs self-signed.
39
+ // axios-cookiejar-support@4 é incompatível com httpsAgent customizado — não usar.
40
+ return (0, axios_cookiejar_support_1.wrapper)(axios_1.default.create({
41
+ baseURL: `${SAP_URL}/sap/bc/adt`,
42
+ jar,
43
+ withCredentials: true,
44
+ auth: { username: exports.SAP_USER, password: SAP_PASS },
45
+ params: { "sap-client": SAP_CLIENT, "sap-language": exports.SAP_LANGUAGE },
46
+ timeout: SAP_TIMEOUT,
47
+ }));
48
+ }
49
+ const http = createHttpClient();
50
+ exports.http = http;
51
+ // ─── Sessão CSRF ──────────────────────────────────────────────────
52
+ /** Máximo de tentativas para obter CSRF token */
53
+ const CSRF_MAX_RETRIES = 3;
54
+ /** Intervalo entre tentativas de CSRF (ms) */
55
+ const CSRF_RETRY_DELAY = 1000;
56
+ async function ensureSession() {
57
+ if (session)
58
+ return session.csrfToken;
59
+ let lastError;
60
+ for (let attempt = 1; attempt <= CSRF_MAX_RETRIES; attempt++) {
61
+ try {
62
+ const response = await http.get("/discovery", {
63
+ headers: { "X-CSRF-Token": "Fetch", Accept: "application/xml" },
64
+ validateStatus: (status) => status < 500, // 406 é esperado mas traz o CSRF token
65
+ });
66
+ const token = response.headers["x-csrf-token"];
67
+ if (token) {
68
+ session = { csrfToken: token };
69
+ return token;
70
+ }
71
+ lastError = new AdtError("CSRF token não retornado pelo SAP ADT.", 0, "/discovery");
72
+ }
73
+ catch (error) {
74
+ lastError = error;
75
+ }
76
+ // Retry com backoff linear (1s, 2s, 3s)
77
+ if (attempt < CSRF_MAX_RETRIES) {
78
+ await new Promise((r) => setTimeout(r, CSRF_RETRY_DELAY * attempt));
79
+ }
80
+ }
81
+ throw lastError instanceof AdtError
82
+ ? lastError
83
+ : new AdtError("Falha ao obter CSRF token do SAP ADT após 3 tentativas. Verifique se o serviço /sap/bc/adt/discovery está ativo.", 0, "/discovery");
84
+ }
85
+ function resetSession() {
86
+ session = null;
87
+ }
88
+ // ─── Erros tipados ────────────────────────────────────────────────
89
+ class AdtError extends Error {
90
+ statusCode;
91
+ path;
92
+ sapMessage;
93
+ constructor(message, statusCode, path, sapMessage) {
94
+ super(message);
95
+ this.statusCode = statusCode;
96
+ this.path = path;
97
+ this.sapMessage = sapMessage;
98
+ this.name = "AdtError";
99
+ }
100
+ }
101
+ exports.AdtError = AdtError;
102
+ function isAxiosError(error) {
103
+ return typeof error === "object" && error !== null && ("response" in error || "code" in error);
104
+ }
105
+ // ─── Mensagens de erro em português ───────────────────────────────
106
+ function translateError(error, path) {
107
+ if (error instanceof AdtError)
108
+ return error;
109
+ if (isAxiosError(error)) {
110
+ const status = error.response?.status ?? 0;
111
+ const sapMsg = typeof error.response?.data === "string"
112
+ ? extractSapMessage(error.response.data)
113
+ : undefined;
114
+ switch (status) {
115
+ case 401:
116
+ return new AdtError("Credenciais inválidas ou sessão expirada. Verifique SAP_USER e SAP_PASS.", 401, path, sapMsg);
117
+ case 403:
118
+ return new AdtError(`Sem autorização para acessar ${path}. Verifique as autorizações do usuário ${exports.SAP_USER} no SAP.`, 403, path, sapMsg);
119
+ case 404:
120
+ return new AdtError(`Objeto não encontrado: ${path}. Verifique o nome e tipo do objeto.`, 404, path, sapMsg);
121
+ case 409:
122
+ return new AdtError(`Conflito ao acessar ${path}. O objeto pode estar bloqueado por outro usuário.`, 409, path, sapMsg);
123
+ case 412:
124
+ return new AdtError(`ETag desatualizado para ${path}. O objeto foi modificado por outro processo. Tente novamente.`, 412, path, sapMsg);
125
+ case 422:
126
+ return new AdtError(sapMsg || `Erro de validação em ${path}. Verifique os dados enviados.`, 422, path, sapMsg);
127
+ default:
128
+ if (status >= 400) {
129
+ return new AdtError(sapMsg || `Erro HTTP ${status} ao acessar ${path}: ${error.response?.statusText ?? "erro desconhecido"}`, status, path, sapMsg);
130
+ }
131
+ }
132
+ // Erros de rede (sem response)
133
+ if (error.code === "ECONNREFUSED") {
134
+ return new AdtError(`Conexão recusada: ${SAP_URL}. Verifique se o SAP está acessível e a VPN está conectada.`, 0, path);
135
+ }
136
+ if (error.code === "ECONNABORTED" || error.code === "ETIMEDOUT") {
137
+ return new AdtError(`Timeout ao acessar ${path} (limite: ${SAP_TIMEOUT / 1000}s). O servidor SAP pode estar lento ou inacessível.`, 0, path);
138
+ }
139
+ if (error.code === "ENOTFOUND") {
140
+ return new AdtError(`Host não encontrado: ${SAP_URL}. Verifique a URL do SAP e a resolução DNS.`, 0, path);
141
+ }
142
+ if (error.code === "UNABLE_TO_VERIFY_LEAF_SIGNATURE" || error.code === "DEPTH_ZERO_SELF_SIGNED_CERT") {
143
+ return new AdtError(`Certificado SSL inválido para ${SAP_URL}. Defina NODE_TLS_REJECT_UNAUTHORIZED=0 no mcp.json.`, 0, path);
144
+ }
145
+ }
146
+ const msg = error instanceof Error ? error.message : String(error);
147
+ return new AdtError(`Erro inesperado em ${path}: ${msg}`, 0, path);
148
+ }
149
+ /** Extrai mensagem de erro do XML de resposta SAP */
150
+ function extractSapMessage(xml) {
151
+ // SAP retorna erros em <exc:exception><exc:message>
152
+ const match = xml.match(/<(?:exc:)?message[^>]*>([^<]+)<\/(?:exc:)?message>/i)
153
+ || xml.match(/<msg[^>]*>([^<]+)<\/msg>/i)
154
+ || xml.match(/<shortText>([^<]+)<\/shortText>/i);
155
+ return match?.[1]?.trim();
156
+ }
157
+ // ─── Retry com logging ────────────────────────────────────────────
158
+ /**
159
+ * Wrapper central: executa uma operação ADT com retry automático em 401
160
+ * e logging de cada chamada.
161
+ */
162
+ async function withRetry(method, path, fn) {
163
+ let lastError;
164
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
165
+ const elapsed = (0, logger_js_1.startTimer)();
166
+ try {
167
+ const result = await fn();
168
+ (0, logger_js_1.logCall)({
169
+ timestamp: new Date().toISOString(),
170
+ method,
171
+ path,
172
+ status: 200,
173
+ duration_ms: elapsed(),
174
+ });
175
+ return result;
176
+ }
177
+ catch (error) {
178
+ const duration = elapsed();
179
+ lastError = error;
180
+ const status = isAxiosError(error) ? (error.response?.status ?? 0) : 0;
181
+ (0, logger_js_1.logCall)({
182
+ timestamp: new Date().toISOString(),
183
+ method,
184
+ path,
185
+ status,
186
+ duration_ms: duration,
187
+ error: error instanceof Error ? error.message : String(error),
188
+ });
189
+ // 401 = sessão expirada → reseta e tenta de novo (apenas 1x)
190
+ if (status === 401 && attempt < MAX_RETRIES) {
191
+ resetSession();
192
+ continue;
193
+ }
194
+ // Timeout → tenta de novo (apenas 1x)
195
+ if (isAxiosError(error) && (error.code === "ECONNABORTED" || error.code === "ETIMEDOUT") && attempt < MAX_RETRIES) {
196
+ continue;
197
+ }
198
+ break;
199
+ }
200
+ }
201
+ throw translateError(lastError, path);
202
+ }
203
+ // ─── Mapeamento de tipo SAP para caminho ADT ─────────────────────
204
+ const TYPE_TO_PATH = {
205
+ // Objetos ABAP clássicos
206
+ "PROG/P": "programs/programs",
207
+ "CLAS/OC": "oo/classes",
208
+ "FUGR/FF": "functions/functionmodules",
209
+ "DOMA/D": "ddic/domains",
210
+ "DTEL/D": "ddic/dataelements",
211
+ "TABL/DT": "ddic/tables",
212
+ "TABL/DS": "ddic/structures",
213
+ "INTF/OI": "oo/interfaces",
214
+ // Objetos RAP / Fiori
215
+ "DDLS/DF": "ddic/ddl/sources",
216
+ "DDLX/MX": "ddic/ddlx/sources",
217
+ "SRVD/SRV": "ddic/srvd/sources",
218
+ "BDEF/BDO": "bo/behaviordefinitions",
219
+ "SRVB/SVB": "businessservices/bindings",
220
+ // Objetos adicionais
221
+ "FUGR/F": "functions/groups",
222
+ "TTYP/TT": "ddic/tabletypes",
223
+ "MSAG/N": "messageclass",
224
+ // Transformações
225
+ "XSLT/XT": "transformations/xslt",
226
+ "XSLT/ST": "transformations/simple",
227
+ // Enhancements
228
+ "ENHS/ES": "enhancements/spots",
229
+ "ENHO/EO": "enhancements/implementations",
230
+ // DDIC adicional
231
+ "ENQU/DL": "ddic/lockobjects/sources",
232
+ "NROB/NR": "numberranges/objects",
233
+ "SUSO/B": "authorization/authorizationobjects",
234
+ };
235
+ function resolveAdtPath(objectType) {
236
+ const path = TYPE_TO_PATH[objectType];
237
+ if (!path)
238
+ throw new AdtError(`Tipo de objeto não suportado: ${objectType}. Tipos válidos: ${Object.keys(TYPE_TO_PATH).join(", ")}`, 0, "");
239
+ return path;
240
+ }
241
+ // ─── Funções ADT com retry e logging ──────────────────────────────
242
+ async function adtGet(path) {
243
+ return withRetry("GET", path, async () => {
244
+ await ensureSession();
245
+ const response = await http.get(path, {
246
+ headers: { Accept: "text/plain" },
247
+ responseType: "text",
248
+ });
249
+ return response.data;
250
+ });
251
+ }
252
+ async function adtGetXml(path) {
253
+ return withRetry("GET_XML", path, async () => {
254
+ await ensureSession();
255
+ const response = await http.get(path, {
256
+ headers: { Accept: "application/xml" },
257
+ responseType: "text",
258
+ });
259
+ return response.data;
260
+ });
261
+ }
262
+ async function adtGetWithEtag(path) {
263
+ return withRetry("GET_ETAG", path, async () => {
264
+ await ensureSession();
265
+ const response = await http.get(path, {
266
+ headers: { Accept: "text/plain" },
267
+ responseType: "text",
268
+ });
269
+ const etag = response.headers["etag"];
270
+ if (!etag)
271
+ throw new AdtError("ETag não retornado pelo servidor — impossível gravar com segurança. Verifique se o objeto suporta edição via ADT.", 0, path);
272
+ return { source: response.data, etag };
273
+ });
274
+ }
275
+ async function adtPut(path, source, etag, transportRequest) {
276
+ await withRetry("PUT", path, async () => {
277
+ const csrf = await ensureSession();
278
+ const params = {};
279
+ if (transportRequest)
280
+ params.corrNr = transportRequest;
281
+ await http.put(path, source, {
282
+ headers: {
283
+ "Content-Type": "text/plain",
284
+ "X-CSRF-Token": csrf,
285
+ "If-Match": etag,
286
+ },
287
+ params,
288
+ });
289
+ });
290
+ }
291
+ async function adtLock(objectPath) {
292
+ return withRetry("LOCK", objectPath, async () => {
293
+ const csrf = await ensureSession();
294
+ const resp = await http.post(objectPath, "", {
295
+ headers: { "X-CSRF-Token": csrf },
296
+ params: { _action: "LOCK", accessMode: "MODIFY" },
297
+ validateStatus: (status) => status < 500,
298
+ responseType: "text",
299
+ });
300
+ if (resp.status >= 300) {
301
+ throw new AdtError(`Falha ao bloquear objeto ${objectPath} (status ${resp.status}). O objeto pode estar bloqueado por outro usuário.`, resp.status, objectPath);
302
+ }
303
+ // Extrair lockHandle do XML de resposta
304
+ const data = typeof resp.data === "string" ? resp.data : "";
305
+ const handleMatch = data.match(/<LOCK_HANDLE>([^<]+)<\/LOCK_HANDLE>/);
306
+ return handleMatch?.[1] ?? "";
307
+ });
308
+ }
309
+ async function adtUnlock(objectPath) {
310
+ await withRetry("UNLOCK", objectPath, async () => {
311
+ const csrf = await ensureSession();
312
+ await http.post(objectPath, "", {
313
+ headers: { "X-CSRF-Token": csrf },
314
+ params: { _action: "UNLOCK" },
315
+ validateStatus: (status) => status < 500,
316
+ });
317
+ });
318
+ }
319
+ async function adtDelete(path, transportRequest) {
320
+ await withRetry("DELETE", path, async () => {
321
+ const csrf = await ensureSession();
322
+ const params = {};
323
+ if (transportRequest)
324
+ params.corrNr = transportRequest;
325
+ await http.delete(path, {
326
+ headers: { "X-CSRF-Token": csrf },
327
+ params,
328
+ });
329
+ });
330
+ }
331
+ async function adtPostXml(path, body, params, accept = "application/xml") {
332
+ return withRetry("POST_XML", path, async () => {
333
+ const csrf = await ensureSession();
334
+ const response = await http.post(path, body, {
335
+ headers: {
336
+ "Content-Type": "application/xml",
337
+ Accept: accept,
338
+ "X-CSRF-Token": csrf,
339
+ },
340
+ params,
341
+ responseType: "text",
342
+ validateStatus: (status) => status < 500,
343
+ });
344
+ return {
345
+ data: response.data,
346
+ status: response.status,
347
+ headers: response.headers,
348
+ };
349
+ });
350
+ }
351
+ async function adtGetXmlWithParams(path, params) {
352
+ return withRetry("GET_XML", path, async () => {
353
+ await ensureSession();
354
+ const response = await http.get(path, {
355
+ headers: { Accept: "application/xml" },
356
+ params,
357
+ responseType: "text",
358
+ });
359
+ return response.data;
360
+ });
361
+ }
362
+ async function adtPostText(path, body, params, accept = "application/xml") {
363
+ return withRetry("POST_TEXT", path, async () => {
364
+ const csrf = await ensureSession();
365
+ const response = await http.post(path, body, {
366
+ headers: {
367
+ "Content-Type": "text/plain",
368
+ Accept: accept,
369
+ "X-CSRF-Token": csrf,
370
+ },
371
+ params,
372
+ responseType: "text",
373
+ validateStatus: (status) => status < 500,
374
+ });
375
+ return {
376
+ data: response.data,
377
+ status: response.status,
378
+ headers: response.headers,
379
+ };
380
+ });
381
+ }
382
+ // ─── Exports ──────────────────────────────────────────────────────
383
+ exports.SUPPORTED_OBJECT_TYPES = Object.keys(TYPE_TO_PATH);
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.readLicense = readLicense;
37
+ exports.activate = activate;
38
+ const crypto = __importStar(require("crypto"));
39
+ const fs = __importStar(require("fs"));
40
+ const os = __importStar(require("os"));
41
+ const path = __importStar(require("path"));
42
+ const LICENSE_DIR = path.join(os.homedir(), ".abap-ai");
43
+ const LICENSE_PATH = path.join(LICENSE_DIR, "license.json");
44
+ // ── Beta key validation (offline, HMAC-based) ───────────────────────
45
+ // Em produção será substituído por fetch ao license server (api.lkpabap.ai)
46
+ const BETA_SECRET = "lkp-beta-2026-xK9mQ3vR";
47
+ const PLAN_MAP = { ST: "starter", PR: "pro", EN: "enterprise" };
48
+ function validateBetaKey(key) {
49
+ const upper = key.toUpperCase();
50
+ const match = upper.match(/^LK-([A-Z]{2})([A-Z]{2})-([A-Z0-9]{4})-([A-Z0-9]{4})$/);
51
+ if (!match)
52
+ return null;
53
+ const [, planCode, modeCode, rand, checksum] = match;
54
+ const plan = PLAN_MAP[planCode];
55
+ if (!plan || modeCode !== "BK")
56
+ return null;
57
+ // Recalcula HMAC e compara com o checksum da chave
58
+ const payload = `${planCode}${modeCode}-${rand}`;
59
+ const expected = crypto
60
+ .createHmac("sha256", BETA_SECRET)
61
+ .update(payload)
62
+ .digest("hex")
63
+ .toUpperCase()
64
+ .slice(0, 4);
65
+ if (checksum !== expected)
66
+ return null;
67
+ return { plan };
68
+ }
69
+ function saveLicense(license) {
70
+ if (!fs.existsSync(LICENSE_DIR)) {
71
+ fs.mkdirSync(LICENSE_DIR, { recursive: true });
72
+ }
73
+ fs.writeFileSync(LICENSE_PATH, JSON.stringify(license, null, 2) + "\n", "utf-8");
74
+ }
75
+ function readLicense() {
76
+ try {
77
+ const raw = fs.readFileSync(LICENSE_PATH, "utf-8");
78
+ return JSON.parse(raw);
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ async function activate(key) {
85
+ console.log("\n ⏳ Validando licença...");
86
+ const keyPattern = /^LK-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/i;
87
+ if (!keyPattern.test(key)) {
88
+ console.log(` ✗ Formato de chave inválido.`);
89
+ console.log(` Formato esperado: LK-XXXX-XXXX-XXXX`);
90
+ console.log(` Adquira em: https://lkpabap.ai\n`);
91
+ process.exit(1);
92
+ }
93
+ // ── Tenta validação offline (beta keys com HMAC) ──────────────────
94
+ // TODO: Quando o license server existir, tentar fetch a api.lkpabap.ai primeiro
95
+ const beta = validateBetaKey(key);
96
+ if (!beta) {
97
+ console.log(` ✗ Chave inválida ou expirada.`);
98
+ console.log(` Verifique a chave recebida ou solicite uma nova.\n`);
99
+ process.exit(1);
100
+ }
101
+ const { plan } = beta;
102
+ const license = {
103
+ key: key.toUpperCase(),
104
+ plan,
105
+ limits: {
106
+ systems: plan === "starter" ? 1 : plan === "pro" ? 3 : -1,
107
+ calls_per_day: plan === "starter" ? 200 : -1,
108
+ },
109
+ expires: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString().split("T")[0],
110
+ validated_at: new Date().toISOString(),
111
+ };
112
+ saveLicense(license);
113
+ const planLabel = `${plan.charAt(0).toUpperCase() + plan.slice(1)} (BYOK)`;
114
+ const systemsLabel = license.limits.systems === -1 ? "Ilimitado" : `até ${license.limits.systems}`;
115
+ const callsLabel = license.limits.calls_per_day === -1 ? "Ilimitado" : `${license.limits.calls_per_day}/dia`;
116
+ console.log(` ✓ Licença válida!`);
117
+ console.log(`
118
+ ╭──────────────────────────────────────────╮
119
+ │ Plano: ${planLabel.padEnd(25)}│
120
+ │ Válido até: ${license.expires.padEnd(25)}│
121
+ │ Sistemas: ${systemsLabel.padEnd(25)}│
122
+ │ Chamadas/dia: ${callsLabel.padEnd(25)}│
123
+ │${" ".repeat(42)}│
124
+ │ Licença salva em ~/.abap-ai/license${" ".repeat(4)}│
125
+ ╰──────────────────────────────────────────╯`);
126
+ console.log(`\n Próximo passo: abap-ai init\n`);
127
+ }