llng-mcp 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 (114) hide show
  1. package/.github/workflows/ci.yml +77 -0
  2. package/.prettierrc +7 -0
  3. package/LICENSE +661 -0
  4. package/README.md +502 -0
  5. package/dist/__tests__/api-transport.test.d.ts +1 -0
  6. package/dist/__tests__/api-transport.test.js +577 -0
  7. package/dist/__tests__/api-transport.test.js.map +1 -0
  8. package/dist/__tests__/config.test.d.ts +1 -0
  9. package/dist/__tests__/config.test.js +472 -0
  10. package/dist/__tests__/config.test.js.map +1 -0
  11. package/dist/__tests__/integration/api-mode.test.d.ts +1 -0
  12. package/dist/__tests__/integration/api-mode.test.js +199 -0
  13. package/dist/__tests__/integration/api-mode.test.js.map +1 -0
  14. package/dist/__tests__/integration/oidc-rp.test.d.ts +1 -0
  15. package/dist/__tests__/integration/oidc-rp.test.js +120 -0
  16. package/dist/__tests__/integration/oidc-rp.test.js.map +1 -0
  17. package/dist/__tests__/integration/ssh-mode.test.d.ts +1 -0
  18. package/dist/__tests__/integration/ssh-mode.test.js +101 -0
  19. package/dist/__tests__/integration/ssh-mode.test.js.map +1 -0
  20. package/dist/__tests__/k8s-transport.test.d.ts +1 -0
  21. package/dist/__tests__/k8s-transport.test.js +254 -0
  22. package/dist/__tests__/k8s-transport.test.js.map +1 -0
  23. package/dist/__tests__/oidc-tools.test.d.ts +1 -0
  24. package/dist/__tests__/oidc-tools.test.js +457 -0
  25. package/dist/__tests__/oidc-tools.test.js.map +1 -0
  26. package/dist/__tests__/registry.test.d.ts +1 -0
  27. package/dist/__tests__/registry.test.js +96 -0
  28. package/dist/__tests__/registry.test.js.map +1 -0
  29. package/dist/__tests__/ssh-transport.test.d.ts +1 -0
  30. package/dist/__tests__/ssh-transport.test.js +618 -0
  31. package/dist/__tests__/ssh-transport.test.js.map +1 -0
  32. package/dist/__tests__/tools.test.d.ts +1 -0
  33. package/dist/__tests__/tools.test.js +525 -0
  34. package/dist/__tests__/tools.test.js.map +1 -0
  35. package/dist/config.d.ts +65 -0
  36. package/dist/config.js +506 -0
  37. package/dist/config.js.map +1 -0
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.js +42 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/resources/documentation.d.ts +5 -0
  42. package/dist/resources/documentation.js +56 -0
  43. package/dist/resources/documentation.js.map +1 -0
  44. package/dist/tools/cli-utilities.d.ts +3 -0
  45. package/dist/tools/cli-utilities.js +187 -0
  46. package/dist/tools/cli-utilities.js.map +1 -0
  47. package/dist/tools/config.d.ts +6 -0
  48. package/dist/tools/config.js +326 -0
  49. package/dist/tools/config.js.map +1 -0
  50. package/dist/tools/consents.d.ts +3 -0
  51. package/dist/tools/consents.js +39 -0
  52. package/dist/tools/consents.js.map +1 -0
  53. package/dist/tools/instances.d.ts +3 -0
  54. package/dist/tools/instances.js +14 -0
  55. package/dist/tools/instances.js.map +1 -0
  56. package/dist/tools/oidc-rp.d.ts +6 -0
  57. package/dist/tools/oidc-rp.js +246 -0
  58. package/dist/tools/oidc-rp.js.map +1 -0
  59. package/dist/tools/oidc.d.ts +3 -0
  60. package/dist/tools/oidc.js +343 -0
  61. package/dist/tools/oidc.js.map +1 -0
  62. package/dist/tools/secondfactors.d.ts +3 -0
  63. package/dist/tools/secondfactors.js +62 -0
  64. package/dist/tools/secondfactors.js.map +1 -0
  65. package/dist/tools/sessions.d.ts +6 -0
  66. package/dist/tools/sessions.js +300 -0
  67. package/dist/tools/sessions.js.map +1 -0
  68. package/dist/transport/api.d.ts +35 -0
  69. package/dist/transport/api.js +327 -0
  70. package/dist/transport/api.js.map +1 -0
  71. package/dist/transport/interface.d.ts +50 -0
  72. package/dist/transport/interface.js +2 -0
  73. package/dist/transport/interface.js.map +1 -0
  74. package/dist/transport/k8s.d.ts +41 -0
  75. package/dist/transport/k8s.js +303 -0
  76. package/dist/transport/k8s.js.map +1 -0
  77. package/dist/transport/registry.d.ts +20 -0
  78. package/dist/transport/registry.js +91 -0
  79. package/dist/transport/registry.js.map +1 -0
  80. package/dist/transport/ssh.d.ts +37 -0
  81. package/dist/transport/ssh.js +353 -0
  82. package/dist/transport/ssh.js.map +1 -0
  83. package/docker-compose.test.yml +16 -0
  84. package/eslint.config.js +21 -0
  85. package/package.json +38 -0
  86. package/src/__tests__/api-transport.test.ts +746 -0
  87. package/src/__tests__/config.test.ts +587 -0
  88. package/src/__tests__/integration/api-mode.test.ts +229 -0
  89. package/src/__tests__/integration/oidc-rp.test.ts +138 -0
  90. package/src/__tests__/integration/ssh-mode.test.ts +113 -0
  91. package/src/__tests__/k8s-transport.test.ts +342 -0
  92. package/src/__tests__/oidc-tools.test.ts +554 -0
  93. package/src/__tests__/registry.test.ts +110 -0
  94. package/src/__tests__/ssh-transport.test.ts +805 -0
  95. package/src/__tests__/tools.test.ts +735 -0
  96. package/src/config.ts +605 -0
  97. package/src/index.ts +48 -0
  98. package/src/resources/documentation.ts +65 -0
  99. package/src/tools/cli-utilities.ts +207 -0
  100. package/src/tools/config.ts +382 -0
  101. package/src/tools/consents.ts +50 -0
  102. package/src/tools/instances.ts +21 -0
  103. package/src/tools/oidc-rp.ts +299 -0
  104. package/src/tools/oidc.ts +434 -0
  105. package/src/tools/secondfactors.ts +78 -0
  106. package/src/tools/sessions.ts +342 -0
  107. package/src/transport/api.ts +429 -0
  108. package/src/transport/interface.ts +58 -0
  109. package/src/transport/k8s.ts +367 -0
  110. package/src/transport/registry.ts +105 -0
  111. package/src/transport/ssh.ts +430 -0
  112. package/tsconfig.json +16 -0
  113. package/vitest.config.ts +8 -0
  114. package/vitest.integration.config.ts +9 -0
@@ -0,0 +1,429 @@
1
+ import {
2
+ ILlngTransport,
3
+ ConfigInfo,
4
+ SessionFilter,
5
+ SessionGetOptions,
6
+ SessionDeleteOptions,
7
+ } from "./interface.js";
8
+ import { ApiConfig } from "../config.js";
9
+ import https from "https";
10
+
11
+ export class ApiTransport implements ILlngTransport {
12
+ private baseUrl: string;
13
+ private basicAuth?: { username: string; password: string };
14
+ private verifySsl: boolean;
15
+ private agent?: https.Agent;
16
+
17
+ constructor(config: ApiConfig) {
18
+ // Strip trailing slash
19
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
20
+ this.basicAuth = config.basicAuth;
21
+ this.verifySsl = config.verifySsl !== false;
22
+
23
+ // Create agent for SSL verification control
24
+ if (!this.verifySsl) {
25
+ console.error(
26
+ "WARNING: SSL certificate verification is disabled. This makes the connection vulnerable to man-in-the-middle attacks. Do not use in production.",
27
+ );
28
+ this.agent = new https.Agent({
29
+ rejectUnauthorized: false,
30
+ });
31
+ }
32
+ }
33
+
34
+ private async request(method: string, path: string, body?: any): Promise<any> {
35
+ const url = `${this.baseUrl}${path}`;
36
+ const headers: Record<string, string> = {
37
+ "Content-Type": "application/json",
38
+ };
39
+
40
+ // Add Basic Auth if configured
41
+ if (this.basicAuth) {
42
+ const credentials = Buffer.from(
43
+ `${this.basicAuth.username}:${this.basicAuth.password}`,
44
+ ).toString("base64");
45
+ headers["Authorization"] = `Basic ${credentials}`;
46
+ }
47
+
48
+ const options: RequestInit = {
49
+ method,
50
+ headers,
51
+ // @ts-expect-error - agent is valid for https URLs
52
+ agent: this.agent,
53
+ };
54
+
55
+ if (body && method !== "GET") {
56
+ options.body = JSON.stringify(body);
57
+ }
58
+
59
+ try {
60
+ const response = await fetch(url, options);
61
+
62
+ if (!response.ok) {
63
+ throw new Error(`HTTP ${response.status} ${response.statusText}`);
64
+ }
65
+
66
+ // Handle empty responses
67
+ const contentType = response.headers.get("content-type");
68
+ if (contentType && contentType.includes("application/json")) {
69
+ return await response.json();
70
+ } else {
71
+ const text = await response.text();
72
+ if (!text) {
73
+ return {};
74
+ }
75
+ try {
76
+ return JSON.parse(text);
77
+ } catch {
78
+ return text;
79
+ }
80
+ }
81
+ } catch (error) {
82
+ if (error instanceof Error) {
83
+ throw new Error(`API request failed: ${error.message}`);
84
+ }
85
+ throw error;
86
+ }
87
+ }
88
+
89
+ async configInfo(): Promise<ConfigInfo> {
90
+ const data = await this.request("GET", "/api/v1/config/latest");
91
+ return {
92
+ cfgNum: data.cfgNum,
93
+ cfgAuthor: data.cfgAuthor,
94
+ cfgDate: data.cfgDate,
95
+ cfgLog: data.cfgLog,
96
+ };
97
+ }
98
+
99
+ async configGet(keys: string[]): Promise<Record<string, any>> {
100
+ const config = await this.request("GET", "/api/v1/config/latest");
101
+ const result: Record<string, any> = {};
102
+
103
+ for (const key of keys) {
104
+ if (key in config) {
105
+ result[key] = config[key];
106
+ }
107
+ }
108
+
109
+ return result;
110
+ }
111
+
112
+ async configSet(pairs: Record<string, any>, log?: string): Promise<void> {
113
+ // Get current config
114
+ const currentConfig = await this.request("GET", "/api/v1/config/latest");
115
+
116
+ // Merge changes
117
+ const updatedConfig = { ...currentConfig, ...pairs };
118
+
119
+ // Add log if provided
120
+ if (log) {
121
+ updatedConfig.cfgLog = log;
122
+ }
123
+
124
+ // Save config
125
+ await this.request("PUT", "/api/v1/config", updatedConfig);
126
+ }
127
+
128
+ async configAddKey(key: string, subkey: string, value: string): Promise<void> {
129
+ // Get current config
130
+ const config = await this.request("GET", "/api/v1/config/latest");
131
+
132
+ // Initialize the key if it doesn't exist
133
+ if (!config[key]) {
134
+ config[key] = {};
135
+ }
136
+
137
+ // Add the subkey
138
+ config[key][subkey] = value;
139
+
140
+ // Save config
141
+ await this.request("PUT", "/api/v1/config", config);
142
+ }
143
+
144
+ async configDelKey(key: string, subkey: string): Promise<void> {
145
+ // Get current config
146
+ const config = await this.request("GET", "/api/v1/config/latest");
147
+
148
+ // Remove the subkey if it exists
149
+ if (config[key] && subkey in config[key]) {
150
+ delete config[key][subkey];
151
+
152
+ // Save config
153
+ await this.request("PUT", "/api/v1/config", config);
154
+ }
155
+ }
156
+
157
+ async configSave(): Promise<string> {
158
+ const config = await this.request("GET", "/api/v1/config/latest");
159
+ return JSON.stringify(config, null, 2);
160
+ }
161
+
162
+ async configRestore(json: string): Promise<void> {
163
+ const config = JSON.parse(json);
164
+ await this.request("PUT", "/api/v1/config", config);
165
+ }
166
+
167
+ async configMerge(json: string): Promise<void> {
168
+ const snippet = JSON.parse(json);
169
+ const currentConfig = await this.request("GET", "/api/v1/config/latest");
170
+
171
+ // Deep merge
172
+ const merged = this.deepMerge(currentConfig, snippet);
173
+
174
+ await this.request("PUT", "/api/v1/config", merged);
175
+ }
176
+
177
+ private deepMerge(target: any, source: any): any {
178
+ const result = { ...target };
179
+
180
+ for (const key in source) {
181
+ if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
182
+ const value = source[key];
183
+ if (value !== null && value instanceof Object && !Array.isArray(value)) {
184
+ result[key] = this.deepMerge(result[key] || {}, value);
185
+ } else {
186
+ result[key] = value;
187
+ }
188
+ }
189
+
190
+ return result;
191
+ }
192
+
193
+ async configRollback(): Promise<void> {
194
+ // Get current config to find cfgNum
195
+ const current = await this.request("GET", "/api/v1/config/latest");
196
+ const currentNum = current.cfgNum;
197
+
198
+ if (currentNum <= 1) {
199
+ throw new Error("Cannot rollback: already at first config");
200
+ }
201
+
202
+ // Get previous config
203
+ const previous = await this.request("GET", `/api/v1/config/${currentNum - 1}`);
204
+
205
+ // Save it as the new current config
206
+ await this.request("PUT", "/api/v1/config", previous);
207
+ }
208
+
209
+ async configUpdateCache(): Promise<void> {
210
+ // No-op for API mode - cache is managed server-side
211
+ }
212
+
213
+ async configTestEmail(_destination: string): Promise<void> {
214
+ throw new Error("configTestEmail is not supported via API. Use SSH or K8s mode.");
215
+ }
216
+
217
+ private resolveBackend(options?: SessionGetOptions): string {
218
+ if (options?.persistent) return "persistent";
219
+ if (options?.refreshTokens) return "oidc";
220
+ return options?.backend || "global";
221
+ }
222
+
223
+ async sessionGet(id: string, options?: SessionGetOptions): Promise<Record<string, any>> {
224
+ const backendName = this.resolveBackend(options);
225
+ // hash option is not supported via API
226
+ return await this.request(
227
+ "GET",
228
+ `/api/v1/sessions/${encodeURIComponent(backendName)}/${encodeURIComponent(id)}`,
229
+ );
230
+ }
231
+
232
+ async sessionSearch(filters: SessionFilter): Promise<any[]> {
233
+ let backend = filters.backend || "global";
234
+ if (filters.persistent) {
235
+ backend = "persistent";
236
+ }
237
+ const queryParams: string[] = [];
238
+
239
+ // Handle refreshTokens: use backend="oidc" and add _type filter
240
+ if (filters.refreshTokens) {
241
+ backend = "oidc";
242
+ const whereClause = filters.where
243
+ ? { ...filters.where, _type: "refresh_token" }
244
+ : { _type: "refresh_token" };
245
+ const whereClauses = Object.entries(whereClause).map(([field, value]) => `${field}=${value}`);
246
+ queryParams.push(`where=${encodeURIComponent(whereClauses.join(" AND "))}`);
247
+ } else if (filters.where) {
248
+ // Build where clause
249
+ const whereClauses = Object.entries(filters.where).map(
250
+ ([field, value]) => `${field}=${value}`,
251
+ );
252
+ queryParams.push(`where=${encodeURIComponent(whereClauses.join(" AND "))}`);
253
+ }
254
+
255
+ // Handle idOnly: select only _session_id
256
+ if (filters.idOnly) {
257
+ queryParams.push("select=_session_id");
258
+ } else if (filters.select && filters.select.length > 0) {
259
+ queryParams.push(`select=${filters.select.join(",")}`);
260
+ }
261
+
262
+ // Build count flag
263
+ if (filters.count) {
264
+ queryParams.push("count=1");
265
+ }
266
+
267
+ const queryString = queryParams.length > 0 ? `?${queryParams.join("&")}` : "";
268
+ const result = await this.request(
269
+ "GET",
270
+ `/api/v1/sessions/${encodeURIComponent(backend)}${queryString}`,
271
+ );
272
+
273
+ // API returns an object with session IDs as keys, convert to array
274
+ if (typeof result === "object" && !Array.isArray(result)) {
275
+ if (filters.idOnly) {
276
+ return Object.keys(result);
277
+ }
278
+ return Object.entries(result).map(([id, data]) => ({
279
+ id,
280
+ ...(data as object),
281
+ }));
282
+ }
283
+
284
+ return result;
285
+ }
286
+
287
+ async sessionDelete(ids: string[], options?: SessionDeleteOptions): Promise<void> {
288
+ const backendName = this.resolveBackend(options);
289
+
290
+ if (options?.where) {
291
+ // Where-based deletion: search first, then delete matching
292
+ const searchFilters: SessionFilter = {
293
+ where: options.where,
294
+ backend: backendName,
295
+ idOnly: true,
296
+ refreshTokens: options.refreshTokens,
297
+ persistent: options.persistent,
298
+ };
299
+ const matchingIds = await this.sessionSearch(searchFilters);
300
+ for (const id of matchingIds) {
301
+ const sessionId = typeof id === "string" ? id : id.id || id._session_id;
302
+ if (sessionId) {
303
+ await this.request(
304
+ "DELETE",
305
+ `/api/v1/sessions/${encodeURIComponent(backendName)}/${encodeURIComponent(sessionId)}`,
306
+ );
307
+ }
308
+ }
309
+ } else {
310
+ for (const id of ids) {
311
+ await this.request(
312
+ "DELETE",
313
+ `/api/v1/sessions/${encodeURIComponent(backendName)}/${encodeURIComponent(id)}`,
314
+ );
315
+ }
316
+ }
317
+ }
318
+
319
+ async sessionSetKey(
320
+ id: string,
321
+ pairs: Record<string, any>,
322
+ options?: SessionGetOptions,
323
+ ): Promise<void> {
324
+ const backendName = this.resolveBackend(options);
325
+ await this.request(
326
+ "PUT",
327
+ `/api/v1/sessions/${encodeURIComponent(backendName)}/${encodeURIComponent(id)}`,
328
+ pairs,
329
+ );
330
+ }
331
+
332
+ async sessionDelKey(id: string, keys: string[], options?: SessionGetOptions): Promise<void> {
333
+ const backendName = this.resolveBackend(options);
334
+ const pairs: Record<string, null> = {};
335
+ for (const key of keys) {
336
+ pairs[key] = null;
337
+ }
338
+ await this.request(
339
+ "PUT",
340
+ `/api/v1/sessions/${encodeURIComponent(backendName)}/${encodeURIComponent(id)}`,
341
+ pairs,
342
+ );
343
+ }
344
+
345
+ async sessionBackup(
346
+ backend?: string,
347
+ refreshTokens?: boolean,
348
+ persistent?: boolean,
349
+ ): Promise<string> {
350
+ let backendName = backend || "global";
351
+ if (persistent) {
352
+ backendName = "persistent";
353
+ }
354
+ let path = `/api/v1/sessions/${encodeURIComponent(backendName)}`;
355
+
356
+ if (refreshTokens) {
357
+ backendName = "oidc";
358
+ path = `/api/v1/sessions/${encodeURIComponent(backendName)}?where=${encodeURIComponent("_type=refresh_token")}`;
359
+ }
360
+
361
+ const sessions = await this.request("GET", path);
362
+ return JSON.stringify(sessions, null, 2);
363
+ }
364
+
365
+ async secondFactorsGet(user: string): Promise<any[]> {
366
+ const result = await this.request("GET", `/api/v1/secondfactors/${encodeURIComponent(user)}`);
367
+
368
+ // API may return object or array, normalize to array
369
+ if (Array.isArray(result)) {
370
+ return result;
371
+ } else if (typeof result === "object") {
372
+ return Object.entries(result).map(([id, data]) => ({
373
+ id,
374
+ ...(data as object),
375
+ }));
376
+ }
377
+
378
+ return [];
379
+ }
380
+
381
+ async secondFactorsDelete(user: string, ids: string[]): Promise<void> {
382
+ for (const id of ids) {
383
+ await this.request(
384
+ "DELETE",
385
+ `/api/v1/secondfactors/${encodeURIComponent(user)}/${encodeURIComponent(id)}`,
386
+ );
387
+ }
388
+ }
389
+
390
+ async secondFactorsDelType(user: string, type: string): Promise<void> {
391
+ // Get all 2FA devices
392
+ const devices = await this.secondFactorsGet(user);
393
+
394
+ // Filter by type and delete
395
+ const toDelete = devices.filter((device: any) => device.type === type);
396
+ const ids = toDelete.map((device: any) => device.id);
397
+
398
+ await this.secondFactorsDelete(user, ids);
399
+ }
400
+
401
+ async consentsGet(user: string): Promise<any[]> {
402
+ const result = await this.request("GET", `/api/v1/consents/${encodeURIComponent(user)}`);
403
+
404
+ // API may return object or array, normalize to array
405
+ if (Array.isArray(result)) {
406
+ return result;
407
+ } else if (typeof result === "object") {
408
+ return Object.entries(result).map(([id, data]) => ({
409
+ id,
410
+ ...(data as object),
411
+ }));
412
+ }
413
+
414
+ return [];
415
+ }
416
+
417
+ async consentsDelete(user: string, ids: string[]): Promise<void> {
418
+ for (const id of ids) {
419
+ await this.request(
420
+ "DELETE",
421
+ `/api/v1/consents/${encodeURIComponent(user)}/${encodeURIComponent(id)}`,
422
+ );
423
+ }
424
+ }
425
+
426
+ async execScript(_scriptName: string, _args: string[]): Promise<string> {
427
+ throw new Error("execScript is not supported via API. Use SSH or K8s mode.");
428
+ }
429
+ }
@@ -0,0 +1,58 @@
1
+ export interface SessionGetOptions {
2
+ backend?: string;
3
+ refreshTokens?: boolean;
4
+ persistent?: boolean;
5
+ hash?: boolean;
6
+ }
7
+
8
+ export interface SessionDeleteOptions extends SessionGetOptions {
9
+ where?: Record<string, string>;
10
+ }
11
+
12
+ export interface SessionFilter {
13
+ where?: Record<string, string>; // field=value pairs
14
+ select?: string[]; // fields to return
15
+ backend?: string; // persistent, oidc, saml, cas
16
+ count?: boolean; // just return count
17
+ refreshTokens?: boolean; // filter for refresh token (offline) sessions only
18
+ persistent?: boolean; // shortcut for backend=persistent
19
+ hash?: boolean; // session ID is original cookie value
20
+ idOnly?: boolean; // return only session IDs
21
+ }
22
+
23
+ export interface ConfigInfo {
24
+ cfgNum: number;
25
+ cfgAuthor: string;
26
+ cfgDate: string;
27
+ cfgLog?: string;
28
+ }
29
+
30
+ export interface ILlngTransport {
31
+ configInfo(): Promise<ConfigInfo>;
32
+ configGet(keys: string[]): Promise<Record<string, any>>;
33
+ configSet(pairs: Record<string, any>, log?: string): Promise<void>;
34
+ configAddKey(key: string, subkey: string, value: string): Promise<void>;
35
+ configDelKey(key: string, subkey: string): Promise<void>;
36
+ configSave(): Promise<string>;
37
+ configRestore(json: string): Promise<void>;
38
+ configMerge(json: string): Promise<void>;
39
+ configRollback(): Promise<void>;
40
+ configUpdateCache(): Promise<void>;
41
+ configTestEmail(destination: string): Promise<void>;
42
+
43
+ sessionGet(id: string, options?: SessionGetOptions): Promise<Record<string, any>>;
44
+ sessionSearch(filters: SessionFilter): Promise<any[]>;
45
+ sessionDelete(ids: string[], options?: SessionDeleteOptions): Promise<void>;
46
+ sessionSetKey(id: string, pairs: Record<string, any>, options?: SessionGetOptions): Promise<void>;
47
+ sessionDelKey(id: string, keys: string[], options?: SessionGetOptions): Promise<void>;
48
+ sessionBackup(backend?: string, refreshTokens?: boolean, persistent?: boolean): Promise<string>;
49
+
50
+ secondFactorsGet(user: string): Promise<any[]>;
51
+ secondFactorsDelete(user: string, ids: string[]): Promise<void>;
52
+ secondFactorsDelType(user: string, type: string): Promise<void>;
53
+
54
+ consentsGet(user: string): Promise<any[]>;
55
+ consentsDelete(user: string, ids: string[]): Promise<void>;
56
+
57
+ execScript(scriptName: string, args: string[]): Promise<string>;
58
+ }