@forestadmin/agent 1.79.5 → 1.81.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.
@@ -6,9 +6,13 @@ import BaseRoute from '../base-route';
6
6
  export default class WorkflowExecutorProxyRoute extends BaseRoute {
7
7
  readonly type = RouteType.PrivateRoute;
8
8
  private readonly executorUrl;
9
+ protected readonly requestTimeoutMs: number;
9
10
  constructor(services: ForestAdminHttpDriverServices, options: AgentOptionsWithDefaults);
10
11
  setupRoutes(router: KoaRouter): void;
11
12
  private handleProxy;
13
+ private buildTargetUrl;
14
+ private executorPath;
15
+ private forwardedHeaders;
12
16
  private forwardRequest;
13
17
  }
14
18
  //# sourceMappingURL=workflow-executor-proxy.d.ts.map
@@ -3,46 +3,96 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ const datasource_toolkit_1 = require("@forestadmin/datasource-toolkit");
6
7
  const http_1 = require("http");
7
8
  const https_1 = require("https");
8
9
  const types_1 = require("../../types");
9
10
  const base_route_1 = __importDefault(require("../base-route"));
11
+ // Any sub-path under this prefix is forwarded verbatim to the executor root, so a new executor
12
+ // route needs no agent change.
13
+ const AGENT_PREFIX = '/_internal/executor';
14
+ // Never forwarded: hop-by-hop headers, Host (Node derives the executor's from the target URL),
15
+ // and length/encoding the HTTP client recomputes.
16
+ const SKIPPED_HEADERS = new Set([
17
+ 'connection',
18
+ 'keep-alive',
19
+ 'transfer-encoding',
20
+ 'upgrade',
21
+ 'te',
22
+ 'trailer',
23
+ 'proxy-authenticate',
24
+ 'proxy-authorization',
25
+ 'host',
26
+ 'content-length',
27
+ ]);
28
+ // Substrings that could let the wildcard escape the executor origin (traversal, encoded dots,
29
+ // backslash, null byte). SSRF hygiene — not a namespace allowlist.
30
+ const UNSAFE_PATH_FRAGMENTS = ['..', '%2e', '%2E', '\\', '\0'];
31
+ const REQUEST_TIMEOUT_MS = 120000;
10
32
  class WorkflowExecutorProxyRoute extends base_route_1.default {
11
33
  constructor(services, options) {
12
34
  super(services, options);
13
35
  this.type = types_1.RouteType.PrivateRoute;
14
- // Remove trailing slash for clean URL joining
36
+ // Overridable so tests can exercise the timeout branch without waiting the full default.
37
+ this.requestTimeoutMs = REQUEST_TIMEOUT_MS;
15
38
  this.executorUrl = new URL(options.workflowExecutorUrl.replace(/\/+$/, ''));
16
39
  }
17
40
  setupRoutes(router) {
18
- router.get('/_internal/workflow-executions/:runId', this.handleProxy.bind(this));
19
- router.post('/_internal/workflow-executions/:runId/trigger', this.handleProxy.bind(this));
41
+ router.all(`${AGENT_PREFIX}/:path(.*)`, this.handleProxy.bind(this));
20
42
  }
21
43
  async handleProxy(context) {
22
- const { runId } = context.params;
23
- const isTrigger = context.method === 'POST';
24
- const qs = context.querystring ? `?${context.querystring}` : '';
25
- const executorRelativeUrl = isTrigger ? `/runs/${runId}/trigger${qs}` : `/runs/${runId}${qs}`;
26
- const targetUrl = new URL(executorRelativeUrl, this.executorUrl);
27
- const forwardedHeaders = {
28
- authorization: context.request.header.authorization,
29
- cookie: context.request.header.cookie,
30
- };
31
- const response = await this.forwardRequest(context.method, targetUrl, context.request.body, forwardedHeaders);
44
+ const targetUrl = this.buildTargetUrl(context);
45
+ const response = await this.forwardRequest({
46
+ method: context.method,
47
+ url: targetUrl,
48
+ // Raw body forwarded verbatim (set by @koa/bodyparser); undefined for GET.
49
+ body: context.method === 'GET' ? undefined : context.request.rawBody,
50
+ headers: this.forwardedHeaders(context),
51
+ });
32
52
  context.response.status = response.status;
53
+ // Forward every executor response header (minus hop-by-hop) so new executor headers never
54
+ // require an agent change — the agent stays a transparent proxy.
55
+ for (const [name, value] of Object.entries(response.headers)) {
56
+ if (value !== undefined && !SKIPPED_HEADERS.has(name.toLowerCase())) {
57
+ context.response.set(name, value);
58
+ }
59
+ }
33
60
  context.response.body = response.body;
34
61
  }
35
- forwardRequest(method, url, body, forwardedHeaders = {}) {
62
+ buildTargetUrl(context) {
63
+ const path = this.executorPath(String(context.params.path ?? ''));
64
+ const qs = context.querystring ? `?${context.querystring}` : '';
65
+ const targetUrl = new URL(`${path}${qs}`, this.executorUrl);
66
+ // Authoritative SSRF check: URL parsing strips control chars the string guard can't see
67
+ // (e.g. a decoded `\t//host` collapses to `//host`), so confirm we never left the executor.
68
+ if (targetUrl.origin !== this.executorUrl.origin) {
69
+ throw new datasource_toolkit_1.NotFoundError('Invalid workflow executor path');
70
+ }
71
+ return targetUrl;
72
+ }
73
+ // First-pass rejection of escape attempts; the authoritative origin check is in buildTargetUrl.
74
+ executorPath(wildcard) {
75
+ const unsafe = wildcard === '' ||
76
+ wildcard.startsWith('/') ||
77
+ UNSAFE_PATH_FRAGMENTS.some(fragment => wildcard.includes(fragment));
78
+ if (unsafe)
79
+ throw new datasource_toolkit_1.NotFoundError('Invalid workflow executor path');
80
+ return `/${wildcard}`;
81
+ }
82
+ forwardedHeaders(context) {
83
+ const headers = {};
84
+ for (const [name, value] of Object.entries(context.request.headers)) {
85
+ if (value !== undefined && !SKIPPED_HEADERS.has(name.toLowerCase())) {
86
+ headers[name] = value;
87
+ }
88
+ }
89
+ return headers;
90
+ }
91
+ forwardRequest(request) {
92
+ const { method, url, body, headers } = request;
36
93
  const requestFn = url.protocol === 'https:' ? https_1.request : http_1.request;
37
- const headers = { 'Content-Type': 'application/json' };
38
- // Forward the caller's auth so the executor's JWT middleware can validate it.
39
- // Agent and executor share the same FOREST_AUTH_SECRET so the token is valid on both.
40
- if (forwardedHeaders.authorization)
41
- headers.Authorization = forwardedHeaders.authorization;
42
- if (forwardedHeaders.cookie)
43
- headers.Cookie = forwardedHeaders.cookie;
44
94
  return new Promise((resolve, reject) => {
45
- const req = requestFn(url, { method, headers }, res => {
95
+ const req = requestFn(url, { method, headers, timeout: this.requestTimeoutMs }, res => {
46
96
  const chunks = [];
47
97
  res.on('data', chunk => chunks.push(chunk));
48
98
  res.on('end', () => {
@@ -54,17 +104,21 @@ class WorkflowExecutorProxyRoute extends base_route_1.default {
54
104
  catch {
55
105
  parsed = raw;
56
106
  }
57
- resolve({ status: res.statusCode ?? types_1.HttpCode.InternalServerError, body: parsed });
107
+ resolve({
108
+ status: res.statusCode ?? types_1.HttpCode.InternalServerError,
109
+ body: parsed,
110
+ headers: res.headers,
111
+ });
58
112
  });
59
113
  res.on('error', reject);
60
114
  });
61
115
  req.on('error', reject);
62
- if (body && method !== 'GET') {
63
- req.write(JSON.stringify(body));
64
- }
116
+ req.on('timeout', () => req.destroy(new Error('Workflow executor request timed out')));
117
+ if (body !== undefined && method !== 'GET')
118
+ req.write(body);
65
119
  req.end();
66
120
  });
67
121
  }
68
122
  }
69
123
  exports.default = WorkflowExecutorProxyRoute;
70
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid29ya2Zsb3ctZXhlY3V0b3ItcHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcm91dGVzL3dvcmtmbG93L3dvcmtmbG93LWV4ZWN1dG9yLXByb3h5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBS0EsK0JBQThDO0FBQzlDLGlDQUFnRDtBQUVoRCx1Q0FBa0Q7QUFDbEQsK0RBQXNDO0FBT3RDLE1BQXFCLDBCQUEyQixTQUFRLG9CQUFTO0lBSS9ELFlBQVksUUFBdUMsRUFBRSxPQUFpQztRQUNwRixLQUFLLENBQUMsUUFBUSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBSmxCLFNBQUksR0FBRyxpQkFBUyxDQUFDLFlBQVksQ0FBQztRQUtyQyw4Q0FBOEM7UUFDOUMsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLEdBQUcsQ0FBQyxPQUFPLENBQUMsbUJBQW1CLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzlFLENBQUM7SUFFRCxXQUFXLENBQUMsTUFBaUI7UUFDM0IsTUFBTSxDQUFDLEdBQUcsQ0FBQyx1Q0FBdUMsRUFBRSxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ2pGLE1BQU0sQ0FBQyxJQUFJLENBQUMsK0NBQStDLEVBQUUsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUM1RixDQUFDO0lBRU8sS0FBSyxDQUFDLFdBQVcsQ0FBQyxPQUFnQjtRQUN4QyxNQUFNLEVBQUUsS0FBSyxFQUFFLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQztRQUNqQyxNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsTUFBTSxLQUFLLE1BQU0sQ0FBQztRQUM1QyxNQUFNLEVBQUUsR0FBRyxPQUFPLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxJQUFJLE9BQU8sQ0FBQyxXQUFXLEVBQUUsQ0FBQyxD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
124
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid29ya2Zsb3ctZXhlY3V0b3ItcHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcm91dGVzL3dvcmtmbG93L3dvcmtmbG93LWV4ZWN1dG9yLXByb3h5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBTUEsd0VBQWdFO0FBQ2hFLCtCQUE4QztBQUM5QyxpQ0FBZ0Q7QUFFaEQsdUNBQWtEO0FBQ2xELCtEQUFzQztBQUV0QywrRkFBK0Y7QUFDL0YsK0JBQStCO0FBQy9CLE1BQU0sWUFBWSxHQUFHLHFCQUFxQixDQUFDO0FBQzNDLCtGQUErRjtBQUMvRixrREFBa0Q7QUFDbEQsTUFBTSxlQUFlLEdBQUcsSUFBSSxHQUFHLENBQUM7SUFDOUIsWUFBWTtJQUNaLFlBQVk7SUFDWixtQkFBbUI7SUFDbkIsU0FBUztJQUNULElBQUk7SUFDSixTQUFTO0lBQ1Qsb0JBQW9CO0lBQ3BCLHFCQUFxQjtJQUNyQixNQUFNO0lBQ04sZ0JBQWdCO0NBQ2pCLENBQUMsQ0FBQztBQUNILDhGQUE4RjtBQUM5RixtRUFBbUU7QUFDbkUsTUFBTSxxQkFBcUIsR0FBRyxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztBQUMvRCxNQUFNLGtCQUFrQixHQUFHLE1BQU8sQ0FBQztBQUVuQyxNQUFxQiwwQkFBMkIsU0FBUSxvQkFBUztJQU0vRCxZQUFZLFFBQXVDLEVBQUUsT0FBaUM7UUFDcEYsS0FBSyxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztRQU5sQixTQUFJLEdBQUcsaUJBQVMsQ0FBQyxZQUFZLENBQUM7UUFFdkMseUZBQXlGO1FBQ3RFLHFCQUFnQixHQUFXLGtCQUFrQixDQUFDO1FBSS9ELElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQztJ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
package/dist/types.d.ts CHANGED
@@ -46,8 +46,8 @@ export type AgentOptions = {
46
46
  useUnsafeActionEndpoint?: boolean;
47
47
  /**
48
48
  * Base URL of the workflow executor to proxy requests to.
49
- * When set, the agent exposes routes at `/_internal/workflow-executions/`
50
- * that forward to the executor, benefiting from the agent's authentication layer.
49
+ * When set, the agent forwards `/_internal/executor/*` to the executor verbatim,
50
+ * benefiting from the agent's authentication layer.
51
51
  * @example 'http://localhost:4001'
52
52
  */
53
53
  workflowExecutorUrl?: string | null;
@@ -3,10 +3,12 @@ import type { Collection } from '@forestadmin/datasource-toolkit';
3
3
  import type { ForestServerCollection } from '@forestadmin/forestadmin-client';
4
4
  export default class SchemaGeneratorCollection {
5
5
  private readonly schemaGeneratorActions;
6
+ private readonly useUnsafeActionEndpoint;
6
7
  constructor(options: AgentOptionsWithDefaults);
7
8
  /** Build forest-server schema for a collection */
8
9
  buildSchema(collection: Collection): Promise<ForestServerCollection>;
9
10
  private buildActions;
11
+ private static assertNoActionSlugCollision;
10
12
  private static buildFields;
11
13
  private static buildSegments;
12
14
  }
@@ -10,6 +10,7 @@ const generator_segments_1 = __importDefault(require("./generator-segments"));
10
10
  class SchemaGeneratorCollection {
11
11
  constructor(options) {
12
12
  this.schemaGeneratorActions = new generator_actions_1.default(options);
13
+ this.useUnsafeActionEndpoint = options.useUnsafeActionEndpoint;
13
14
  }
14
15
  /** Build forest-server schema for a collection */
15
16
  async buildSchema(collection) {
@@ -28,9 +29,23 @@ class SchemaGeneratorCollection {
28
29
  };
29
30
  }
30
31
  buildActions(collection) {
31
- return Promise.all(Object.keys(collection.schema.actions)
32
- .sort()
33
- .map(name => this.schemaGeneratorActions.buildSchema(collection, name)));
32
+ const names = Object.keys(collection.schema.actions).sort();
33
+ if (this.useUnsafeActionEndpoint) {
34
+ SchemaGeneratorCollection.assertNoActionSlugCollision(collection.name, names);
35
+ }
36
+ return Promise.all(names.map(name => this.schemaGeneratorActions.buildSchema(collection, name)));
37
+ }
38
+ static assertNoActionSlugCollision(collectionName, names) {
39
+ const nameBySlug = new Map();
40
+ names.forEach(name => {
41
+ const slug = generator_actions_1.default.getActionSlug(name);
42
+ const existing = nameBySlug.get(slug);
43
+ if (existing) {
44
+ throw new Error(`Actions "${existing}" and "${name}" on collection "${collectionName}" resolve to the ` +
45
+ `same endpoint slug "${slug}". Rename one of them or disable useUnsafeActionEndpoint.`);
46
+ }
47
+ nameBySlug.set(slug, name);
48
+ });
34
49
  }
35
50
  static buildFields(collection) {
36
51
  // Do not export foreign keys as those will be edited using the many to one relationship.
@@ -48,4 +63,4 @@ class SchemaGeneratorCollection {
48
63
  }
49
64
  }
50
65
  exports.default = SchemaGeneratorCollection;
51
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2VuZXJhdG9yLWNvbGxlY3Rpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvdXRpbHMvZm9yZXN0LXNjaGVtYS9nZW5lcmF0b3ItY29sbGVjdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQVNBLHdFQUE4RDtBQUU5RCw0RUFBeUQ7QUFDekQsMEVBQXVEO0FBQ3ZELDhFQUEyRDtBQUUzRCxNQUFxQix5QkFBeUI7SUFHNUMsWUFBWSxPQUFpQztRQUMzQyxJQUFJLENBQUMsc0JBQXNCLEdBQUcsSUFBSSwyQkFBc0IsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUNwRSxDQUFDO0lBRUQsa0RBQWtEO0lBQ2xELEtBQUssQ0FBQyxXQUFXLENBQUMsVUFBc0I7UUFDdEMsT0FBTztZQUNMLE9BQU8sRUFBRSxNQUFNLElBQUksQ0FBQyxZQUFZLENBQUMsVUFBVSxDQUFDO1lBQzVDLE1BQU0sRUFBRSx5QkFBeUIsQ0FBQyxXQUFXLENBQUMsVUFBVSxDQUFDO1lBQ3pELElBQUksRUFBRSxJQUFJO1lBQ1YsV0FBVyxFQUFFLElBQUk7WUFDakIsVUFBVSxFQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxLQUFLLENBQ3ZELEtBQUssQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxRQUFRLElBQUksS0FBSyxDQUFDLFVBQVUsQ0FDckQ7WUFDRCxZQUFZLEVBQUUsVUFBVSxDQUFDLE1BQU0sQ0FBQyxVQUFVO1lBQzFDLFNBQVMsRUFBRSxLQUFLO1lBQ2hCLElBQUksRUFBRSxVQUFVLENBQUMsSUFBSTtZQUNyQixvQkFBb0IsRUFBRSxLQUFLO1lBQzNCLGNBQWMsRUFBRSxNQUFNO1lBQ3RCLFFBQVEsRUFBRSx5QkFBeUIsQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUFDO1NBQzlELENBQUM7SUFDSixDQUFDO0lBRU8sWUFBWSxDQUFDLFVBQXNCO1FBQ3pDLE9BQU8sT0FBTyxDQUFDLEdBQUcsQ0FDaEIsTUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQzthQUNuQyxJQUFJLEVBQUU7YUFDTixHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsc0JBQXNCLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUMxRSxDQUFDO0lBQ0osQ0FBQztJQUVPLE1BQU0sQ0FBQyxXQUFXLENBQUMsVUFBc0I7UUFDL0MseUZBQXlGO1FBQ3pGLDhGQUE4RjtRQUM5RixPQUFPLE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUM7YUFDekMsTUFBTSxDQUNMLElBQUksQ0FBQyxFQUFFLENBQ0wsZ0NBQVcsQ0FBQyxZQUFZLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUM7WUFDakQsQ0FBQyxnQ0FBVyxDQUFDLFlBQVksQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUNyRDthQUNBLElBQUksRUFBRTthQUNOLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLDBCQUFxQixDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUN0RSxDQUFDO0lBRU8sTUFBTSxDQUFDLGFBQWEsQ0FBQyxVQUFzQjtRQUNqRCxPQUFPLFVBQVUsQ0FBQyxNQUFNLENBQUMsUUFBUTthQUM5QixJQUFJLEVBQUU7YUFDTixHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyw0QkFBdUIsQ0FBQyxXQUFXLENBQUMsVUFBVSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDeEUsQ0FBQztDQUNGO0FBcERELDRDQW9EQyJ9
66
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2VuZXJhdG9yLWNvbGxlY3Rpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvdXRpbHMvZm9yZXN0LXNjaGVtYS9nZW5lcmF0b3ItY29sbGVjdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQVNBLHdFQUE4RDtBQUU5RCw0RUFBeUQ7QUFDekQsMEVBQXVEO0FBQ3ZELDhFQUEyRDtBQUUzRCxNQUFxQix5QkFBeUI7SUFJNUMsWUFBWSxPQUFpQztRQUMzQyxJQUFJLENBQUMsc0JBQXNCLEdBQUcsSUFBSSwyQkFBc0IsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNsRSxJQUFJLENBQUMsdUJBQXVCLEdBQUcsT0FBTyxDQUFDLHVCQUF1QixDQUFDO0lBQ2pFLENBQUM7SUFFRCxrREFBa0Q7SUFDbEQsS0FBSyxDQUFDLFdBQVcsQ0FBQyxVQUFzQjtRQUN0QyxPQUFPO1lBQ0wsT0FBTyxFQUFFLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxVQUFVLENBQUM7WUFDNUMsTUFBTSxFQUFFLHlCQUF5QixDQUFDLFdBQVcsQ0FBQyxVQUFVLENBQUM7WUFDekQsSUFBSSxFQUFFLElBQUk7WUFDVixXQUFXLEVBQUUsSUFBSTtZQUNqQixVQUFVLEVBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEtBQUssQ0FDdkQsS0FBSyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUMsVUFBVSxDQUNyRDtZQUNELFlBQVksRUFBRSxVQUFVLENBQUMsTUFBTSxDQUFDLFVBQVU7WUFDMUMsU0FBUyxFQUFFLEtBQUs7WUFDaEIsSUFBSSxFQUFFLFVBQVUsQ0FBQyxJQUFJO1lBQ3JCLG9CQUFvQixFQUFFLEtBQUs7WUFDM0IsY0FBYyxFQUFFLE1BQU07WUFDdEIsUUFBUSxFQUFFLHlCQUF5QixDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUM7U0FDOUQsQ0FBQztJQUNKLENBQUM7SUFFTyxZQUFZLENBQUMsVUFBc0I7UUFDekMsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO1FBRTVELElBQUksSUFBSSxDQUFDLHVCQUF1QixFQUFFLENBQUM7WUFDakMseUJBQXlCLENBQUMsMkJBQTJCLENBQUMsVUFBVSxDQUFDLElBQUksRUFBRSxLQUFLLENBQUMsQ0FBQztRQUNoRixDQUFDO1FBRUQsT0FBTyxPQUFPLENBQUMsR0FBRyxDQUNoQixLQUFLLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLHNCQUFzQixDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FDN0UsQ0FBQztJQUNKLENBQUM7SUFFTyxNQUFNLENBQUMsMkJBQTJCLENBQUMsY0FBc0IsRUFBRSxLQUFlO1FBQ2hGLE1BQU0sVUFBVSxHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO1FBRTdDLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUU7WUFDbkIsTUFBTSxJQUFJLEdBQUcsMkJBQXNCLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3hELE1BQU0sUUFBUSxHQUFHLFVBQVUsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7WUFFdEMsSUFBSSxRQUFRLEVBQUUsQ0FBQztnQkFDYixNQUFNLElBQUksS0FBSyxDQUNiLFlBQVksUUFBUSxVQUFVLElBQUksb0JBQW9CLGNBQWMsbUJBQW1CO29CQUNyRix1QkFBdUIsSUFBSSwyREFBMkQsQ0FDekYsQ0FBQztZQUNKLENBQUM7WUFFRCxVQUFVLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztRQUM3QixDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFTyxNQUFNLENBQUMsV0FBVyxDQUFDLFVBQXNCO1FBQy9DLHlGQUF5RjtRQUN6Riw4RkFBOEY7UUFDOUYsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDO2FBQ3pDLE1BQU0sQ0FDTCxJQUFJLENBQUMsRUFBRSxDQUNMLGdDQUFXLENBQUMsWUFBWSxDQUFDLFVBQVUsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDO1lBQ2pELENBQUMsZ0NBQVcsQ0FBQyxZQUFZLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FDckQ7YUFDQSxJQUFJLEVBQUU7YUFDTixHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQywwQkFBcUIsQ0FBQyxXQUFXLENBQUMsVUFBVSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDdEUsQ0FBQztJQUVPLE1BQU0sQ0FBQyxhQUFhLENBQUMsVUFBc0I7UUFDakQsT0FBTyxVQUFVLENBQUMsTUFBTSxDQUFDLFFBQVE7YUFDOUIsSUFBSSxFQUFFO2FBQ04sR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsNEJBQXVCLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQ3hFLENBQUM7Q0FDRjtBQTVFRCw0Q0E0RUMifQ==
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forestadmin/agent",
3
- "version": "1.79.5",
3
+ "version": "1.81.0",
4
4
  "main": "dist/index.js",
5
5
  "license": "GPL-3.0",
6
6
  "publishConfig": {