@lowdefy/api 5.6.0 → 6.0.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 (47) hide show
  1. package/dist/context/createAuthorize.js +4 -1
  2. package/dist/context/resolveStrategyCaller.js +56 -0
  3. package/dist/index.js +12 -2
  4. package/dist/routes/agent/callAgent.js +7 -184
  5. package/dist/routes/agent/prepareAgent.js +173 -0
  6. package/dist/routes/auth/createLogger.js +2 -5
  7. package/dist/routes/auth/createPrefixedCookies.js +52 -0
  8. package/dist/routes/auth/{getNextAuthConfig.js → getAuthConfig.js} +27 -16
  9. package/dist/routes/auth/resolveCookies.js +37 -0
  10. package/dist/routes/auth/strategies/createAuthStrategies.js +77 -0
  11. package/dist/routes/auth/strategies/getAuthStrategies.js +31 -0
  12. package/dist/routes/endpoints/authorizeApiEndpoint.js +8 -2
  13. package/dist/routes/endpoints/callEndpoint.js +26 -3
  14. package/dist/routes/endpoints/control/controlReject.js +3 -1
  15. package/dist/routes/endpoints/control/controlThrow.js +4 -1
  16. package/dist/routes/endpoints/findSchedule.js +35 -0
  17. package/dist/routes/endpoints/forwardScheduledEndpoint.js +106 -0
  18. package/dist/routes/endpoints/getEndpointConfig.js +9 -3
  19. package/dist/routes/endpoints/getEnvironmentSchedules.js +29 -0
  20. package/dist/routes/endpoints/handleAgentCall.js +83 -0
  21. package/dist/routes/endpoints/handleEndpointCall.js +49 -1
  22. package/dist/routes/endpoints/handleRenderNotification.js +189 -0
  23. package/dist/routes/endpoints/handleValidateSchema.js +3 -1
  24. package/dist/routes/endpoints/isUnauthenticatedHuman.js +31 -0
  25. package/dist/routes/endpoints/resolveCronEnvironment.js +37 -0
  26. package/dist/routes/endpoints/runDetachedEndpoint.js +66 -0
  27. package/dist/routes/endpoints/runRoutine.js +12 -0
  28. package/dist/routes/endpoints/runScheduledEndpoint.js +104 -0
  29. package/dist/routes/endpoints/runWebhookEndpoint.js +83 -0
  30. package/dist/routes/endpoints/scheduleBackground.js +48 -0
  31. package/dist/routes/mcp/createMcpServer.js +160 -0
  32. package/dist/routes/notifications/derivePreview.js +30 -0
  33. package/dist/routes/notifications/getNotificationConfig.js +32 -0
  34. package/dist/routes/notifications/resolveNotificationLinks.js +72 -0
  35. package/dist/routes/notifications/resolveThemeLogo.js +35 -0
  36. package/dist/routes/page/dynamic/resolveDynamicContent.js +172 -0
  37. package/dist/routes/page/dynamic/unescapeOperators.js +36 -0
  38. package/dist/routes/page/dynamic/validateFragment.js +104 -0
  39. package/dist/routes/page/getPageConfig.js +16 -6
  40. package/dist/routes/websocket/authorizeWebsocket.js +27 -0
  41. package/dist/routes/websocket/createChannelRegistry.js +269 -0
  42. package/dist/routes/websocket/createWebSocketConnection.js +131 -0
  43. package/dist/routes/websocket/getWebsocketConfig.js +30 -0
  44. package/dist/routes/websocket/getWebsocketResolver.js +33 -0
  45. package/dist/routes/websocket/prepareChannel.js +72 -0
  46. package/dist/test/testContext.js +4 -2
  47. package/package.json +12 -10
@@ -0,0 +1,27 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
+ function authorizeWebsocket({ authorize, logger }, { websocketConfig }) {
17
+ if (!authorize(websocketConfig)) {
18
+ logger.debug({
19
+ event: 'debug_websocket_authorize',
20
+ authorized: false,
21
+ auth_config: websocketConfig.auth
22
+ });
23
+ // Same message as a missing websocket so channel existence does not leak.
24
+ throw new ConfigError(`Websocket "${websocketConfig.websocketId}" does not exist.`);
25
+ }
26
+ }
27
+ export default authorizeWebsocket;
@@ -0,0 +1,269 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import crypto from 'crypto';
16
+ import { serializer } from '@lowdefy/helpers';
17
+ import { ConfigError, PluginError, ServiceError } from '@lowdefy/errors';
18
+ import prepareChannel from './prepareChannel.js';
19
+ const MAX_RETRIES = 5;
20
+ const RETRY_BASE_MS = 1000;
21
+ const HEALTHY_RESET_MS = 60 * 1000;
22
+ // One registry per server process. The unit of sharing is the evaluated
23
+ // subscription: subscribers whose evaluated connection and websocket
24
+ // properties are identical join the same channel entry and share one running
25
+ // source resolver. Sources start on the first subscriber and stop when the
26
+ // last one leaves.
27
+ function createChannelRegistry() {
28
+ const channels = new Map();
29
+ function getChannelKey({ connectionProperties, properties, websocketId }) {
30
+ const hash = crypto.createHash('sha1').update(serializer.serializeToString({
31
+ connectionProperties,
32
+ properties
33
+ })).digest('base64');
34
+ return `${websocketId}:${hash}`;
35
+ }
36
+ function broadcast(channel, frame) {
37
+ const message = JSON.stringify(frame);
38
+ channel.subscribers.forEach((subscriber)=>{
39
+ subscriber.send(message);
40
+ });
41
+ }
42
+ function wrapResolverError({ channel, error }) {
43
+ if (!error.configKey) {
44
+ error.configKey = channel.websocketConfig['~k'];
45
+ }
46
+ if (error.isLowdefyError) {
47
+ return error;
48
+ }
49
+ if (ServiceError.isServiceError(error)) {
50
+ return new ServiceError(undefined, {
51
+ cause: error,
52
+ service: channel.websocketConfig.connectionId ?? channel.websocketConfig.type,
53
+ configKey: channel.websocketConfig['~k']
54
+ });
55
+ }
56
+ return new PluginError(error.message, {
57
+ cause: error,
58
+ typeName: channel.websocketConfig.type,
59
+ received: channel.properties,
60
+ location: channel.websocketConfig.websocketId,
61
+ configKey: channel.websocketConfig['~k']
62
+ });
63
+ }
64
+ function removeChannel(channel) {
65
+ if (channel.restartTimer) {
66
+ clearTimeout(channel.restartTimer);
67
+ channel.restartTimer = null;
68
+ }
69
+ channel.abortController?.abort();
70
+ channels.delete(channel.key);
71
+ }
72
+ function handleResolverError({ channel, error, startedAt }) {
73
+ if (channel.abortController?.signal.aborted) {
74
+ // Errors thrown while winding down are expected (closed cursors, etc).
75
+ channel.logger.debug({
76
+ event: 'ws_source_stopped_error_ignored',
77
+ websocketId: channel.websocketId
78
+ }, error.message);
79
+ return;
80
+ }
81
+ const wrapped = wrapResolverError({
82
+ channel,
83
+ error
84
+ });
85
+ channel.logger.debug({
86
+ err: wrapped
87
+ }, wrapped.message);
88
+ channel.context.handleError(wrapped);
89
+ broadcast(channel, {
90
+ type: 'error',
91
+ websocketId: channel.websocketId,
92
+ message: wrapped.message
93
+ });
94
+ // A run that stayed healthy resets the backoff window.
95
+ if (Date.now() - startedAt > HEALTHY_RESET_MS) {
96
+ channel.retryCount = 0;
97
+ }
98
+ if (channel.retryCount >= MAX_RETRIES) {
99
+ channel.logger.error({
100
+ event: 'ws_source_max_retries',
101
+ websocketId: channel.websocketId
102
+ });
103
+ removeChannel(channel);
104
+ return;
105
+ }
106
+ const delay = RETRY_BASE_MS * 2 ** channel.retryCount;
107
+ channel.retryCount += 1;
108
+ channel.restartTimer = setTimeout(()=>{
109
+ channel.restartTimer = null;
110
+ // Subscribers may all have left while the restart was pending.
111
+ if (channel.subscribers.size === 0) {
112
+ removeChannel(channel);
113
+ return;
114
+ }
115
+ channel.logger.info({
116
+ event: 'ws_source_restart',
117
+ websocketId: channel.websocketId,
118
+ retryCount: channel.retryCount
119
+ });
120
+ startResolver(channel);
121
+ }, delay);
122
+ }
123
+ function startResolver(channel) {
124
+ const abortController = new AbortController();
125
+ channel.abortController = abortController;
126
+ const startedAt = Date.now();
127
+ function publish({ data }) {
128
+ if (abortController.signal.aborted) {
129
+ return;
130
+ }
131
+ broadcast(channel, {
132
+ type: 'message',
133
+ websocketId: channel.websocketId,
134
+ payload: serializer.serialize({
135
+ data
136
+ })
137
+ });
138
+ }
139
+ channel.publish = publish;
140
+ Promise.resolve().then(()=>channel.resolver({
141
+ connection: channel.connectionProperties,
142
+ properties: channel.properties,
143
+ publish,
144
+ signal: abortController.signal,
145
+ logger: channel.logger
146
+ })).catch((error)=>handleResolverError({
147
+ channel,
148
+ error,
149
+ startedAt
150
+ }));
151
+ }
152
+ async function subscribe(context, { websocketId, payload, subscriber }) {
153
+ const { connectionProperties, properties, websocketConfig, websocketResolver } = await prepareChannel(context, {
154
+ websocketId,
155
+ payload
156
+ });
157
+ // One subscription per websocketId per connection — a re-subscribe (e.g.
158
+ // with a new payload) replaces the previous one.
159
+ if (subscriber.subscriptions.has(websocketId)) {
160
+ unsubscribe({
161
+ websocketId,
162
+ subscriber
163
+ });
164
+ }
165
+ const key = getChannelKey({
166
+ connectionProperties,
167
+ properties,
168
+ websocketId
169
+ });
170
+ let channel = channels.get(key);
171
+ if (!channel) {
172
+ channel = {
173
+ key,
174
+ websocketId,
175
+ websocketConfig,
176
+ resolver: websocketResolver,
177
+ connectionProperties,
178
+ properties,
179
+ subscribers: new Set(),
180
+ abortController: null,
181
+ restartTimer: null,
182
+ retryCount: 0,
183
+ publish: null,
184
+ context,
185
+ logger: context.logger
186
+ };
187
+ channels.set(key, channel);
188
+ startResolver(channel);
189
+ }
190
+ channel.subscribers.add(subscriber);
191
+ subscriber.subscriptions.set(websocketId, key);
192
+ context.logger.debug({
193
+ event: 'ws_subscribe',
194
+ websocketId,
195
+ subscribers: channel.subscribers.size
196
+ });
197
+ }
198
+ function unsubscribe({ websocketId, subscriber }) {
199
+ const key = subscriber.subscriptions.get(websocketId);
200
+ subscriber.subscriptions.delete(websocketId);
201
+ if (!key) {
202
+ return;
203
+ }
204
+ const channel = channels.get(key);
205
+ if (!channel) {
206
+ return;
207
+ }
208
+ channel.subscribers.delete(subscriber);
209
+ if (channel.subscribers.size === 0) {
210
+ removeChannel(channel);
211
+ }
212
+ }
213
+ function unsubscribeAll({ subscriber }) {
214
+ [
215
+ ...subscriber.subscriptions.keys()
216
+ ].forEach((websocketId)=>{
217
+ unsubscribe({
218
+ websocketId,
219
+ subscriber
220
+ });
221
+ });
222
+ }
223
+ async function publish(context, { websocketId, payload }) {
224
+ // Publish identity is evaluated without a subscription payload — channels
225
+ // that fragment on _payload/_user in properties are not publish targets.
226
+ const { connectionProperties, properties, websocketConfig, websocketResolver } = await prepareChannel(context, {
227
+ websocketId,
228
+ payload: {}
229
+ });
230
+ if (websocketResolver.meta?.publish !== true || properties.publish !== true) {
231
+ throw new ConfigError(`Websocket "${websocketId}" does not allow publishing.`, {
232
+ configKey: websocketConfig['~k']
233
+ });
234
+ }
235
+ const key = getChannelKey({
236
+ connectionProperties,
237
+ properties,
238
+ websocketId
239
+ });
240
+ const channel = channels.get(key);
241
+ // No channel means no subscribers on this instance — the publish is
242
+ // accepted and simply reaches nobody here.
243
+ if (!channel?.publish) {
244
+ return;
245
+ }
246
+ const data = serializer.deserialize(payload);
247
+ if (typeof websocketResolver.onPublish === 'function') {
248
+ await websocketResolver.onPublish({
249
+ data,
250
+ properties,
251
+ publish: channel.publish,
252
+ user: context.user
253
+ });
254
+ return;
255
+ }
256
+ channel.publish({
257
+ data
258
+ });
259
+ }
260
+ return {
261
+ publish,
262
+ subscribe,
263
+ unsubscribe,
264
+ unsubscribeAll,
265
+ // Exposed for tests and shutdown.
266
+ channels
267
+ };
268
+ }
269
+ export default createChannelRegistry;
@@ -0,0 +1,131 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import { type } from '@lowdefy/helpers';
16
+ // Wraps one client websocket connection: parses frames, dispatches
17
+ // subscribe/unsubscribe/publish to the channel registry, and answers every
18
+ // frame with an ack or an error so client actions never hang.
19
+ function createWebSocketConnection(context, { registry, send }) {
20
+ const { logger } = context;
21
+ const subscriber = {
22
+ id: context.rid,
23
+ subscriptions: new Map(),
24
+ send
25
+ };
26
+ function sendError({ message, requestId, websocketId }) {
27
+ send(JSON.stringify({
28
+ type: 'error',
29
+ websocketId,
30
+ requestId,
31
+ message
32
+ }));
33
+ }
34
+ async function handleFrame(frame) {
35
+ const { payload, requestId, websocketId } = frame;
36
+ if (!type.isString(websocketId)) {
37
+ sendError({
38
+ message: 'Frame "websocketId" should be a string.',
39
+ requestId
40
+ });
41
+ return;
42
+ }
43
+ switch(frame.type){
44
+ case 'subscribe':
45
+ await registry.subscribe(context, {
46
+ websocketId,
47
+ payload,
48
+ subscriber
49
+ });
50
+ send(JSON.stringify({
51
+ type: 'subscribed',
52
+ websocketId
53
+ }));
54
+ return;
55
+ case 'unsubscribe':
56
+ registry.unsubscribe({
57
+ websocketId,
58
+ subscriber
59
+ });
60
+ send(JSON.stringify({
61
+ type: 'unsubscribed',
62
+ websocketId
63
+ }));
64
+ return;
65
+ case 'publish':
66
+ await registry.publish(context, {
67
+ websocketId,
68
+ payload
69
+ });
70
+ send(JSON.stringify({
71
+ type: 'published',
72
+ websocketId,
73
+ requestId
74
+ }));
75
+ return;
76
+ default:
77
+ // Unknown frame types are ignored for forward compatibility.
78
+ logger.debug({
79
+ event: 'ws_unknown_frame',
80
+ frameType: frame.type
81
+ });
82
+ }
83
+ }
84
+ async function handleMessage(raw) {
85
+ let frame;
86
+ try {
87
+ frame = JSON.parse(raw);
88
+ } catch (error) {
89
+ sendError({
90
+ message: 'Invalid frame — expected JSON.'
91
+ });
92
+ return;
93
+ }
94
+ if (!type.isObject(frame)) {
95
+ sendError({
96
+ message: 'Invalid frame — expected an object.'
97
+ });
98
+ return;
99
+ }
100
+ try {
101
+ await handleFrame(frame);
102
+ } catch (error) {
103
+ logger.debug({
104
+ err: error
105
+ }, error.message);
106
+ context.handleError(error);
107
+ sendError({
108
+ message: error.message,
109
+ requestId: frame.requestId,
110
+ websocketId: frame.websocketId
111
+ });
112
+ }
113
+ }
114
+ function close() {
115
+ registry.unsubscribeAll({
116
+ subscriber
117
+ });
118
+ logger.debug({
119
+ event: 'ws_disconnect'
120
+ });
121
+ }
122
+ logger.debug({
123
+ event: 'ws_connect'
124
+ });
125
+ return {
126
+ close,
127
+ handleMessage,
128
+ subscriber
129
+ };
130
+ }
131
+ export default createWebSocketConnection;
@@ -0,0 +1,30 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
+ async function getWebsocketConfig({ logger, readConfigFile }, { websocketId }) {
17
+ const websocketConfig = await readConfigFile(`websockets/${websocketId}.json`);
18
+ if (!websocketConfig) {
19
+ const err = new ConfigError(`Websocket "${websocketId}" does not exist.`);
20
+ logger.debug({
21
+ params: {
22
+ websocketId
23
+ },
24
+ err
25
+ }, err.message);
26
+ throw err;
27
+ }
28
+ return websocketConfig;
29
+ }
30
+ export default getWebsocketConfig;
@@ -0,0 +1,33 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
+ function getWebsocketResolver({ logger, websockets }, { websocketConfig }) {
17
+ const websocketResolver = (websockets ?? {})[websocketConfig.type];
18
+ if (!websocketResolver) {
19
+ const err = new ConfigError(`Websocket type "${websocketConfig.type}" can not be found.`, {
20
+ configKey: websocketConfig['~k']
21
+ });
22
+ logger.debug({
23
+ params: {
24
+ id: websocketConfig.websocketId,
25
+ type: websocketConfig.type
26
+ },
27
+ err
28
+ }, err.message);
29
+ throw err;
30
+ }
31
+ return websocketResolver;
32
+ }
33
+ export default getWebsocketResolver;
@@ -0,0 +1,72 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import { type } from '@lowdefy/helpers';
16
+ import authorizeWebsocket from './authorizeWebsocket.js';
17
+ import getConnection from '../connections/getConnection.js';
18
+ import getConnectionConfig from '../connections/getConnectionConfig.js';
19
+ import getWebsocketConfig from './getWebsocketConfig.js';
20
+ import getWebsocketResolver from './getWebsocketResolver.js';
21
+ import createEvaluateOperators from '../../context/createEvaluateOperators.js';
22
+ // Shared preparation for subscribe and publish frames: load config, authorize,
23
+ // resolve the websocket type, and evaluate server operators per subscription.
24
+ // Properties are evaluated with the subscriber's payload and user, so the same
25
+ // websocket definition can produce user- or filter-specific channels. The
26
+ // evaluated result is the channel identity — subscribers whose evaluation is
27
+ // identical share one running source.
28
+ async function prepareChannel(context, { websocketId, payload }) {
29
+ context.evaluateOperators = createEvaluateOperators(context);
30
+ const websocketConfig = await getWebsocketConfig(context, {
31
+ websocketId
32
+ });
33
+ authorizeWebsocket(context, {
34
+ websocketConfig
35
+ });
36
+ const websocketResolver = getWebsocketResolver(context, {
37
+ websocketConfig
38
+ });
39
+ let connectionProperties = null;
40
+ if (!type.isNone(websocketConfig.connectionId)) {
41
+ const connectionConfig = await getConnectionConfig(context, {
42
+ connectionId: websocketConfig.connectionId,
43
+ configKey: websocketConfig['~k']
44
+ });
45
+ // Validates the connection type exists — resolver lookup is on the
46
+ // websocket type itself, not nested on the connection.
47
+ getConnection(context, {
48
+ connectionConfig
49
+ });
50
+ connectionProperties = context.evaluateOperators({
51
+ input: connectionConfig.properties ?? {},
52
+ location: connectionConfig.connectionId,
53
+ payload,
54
+ state: {},
55
+ steps: {}
56
+ });
57
+ }
58
+ const properties = context.evaluateOperators({
59
+ input: websocketConfig.properties ?? {},
60
+ location: websocketConfig.websocketId,
61
+ payload,
62
+ state: {},
63
+ steps: {}
64
+ });
65
+ return {
66
+ connectionProperties,
67
+ properties,
68
+ websocketConfig,
69
+ websocketResolver
70
+ };
71
+ }
72
+ export default prepareChannel;
@@ -20,11 +20,12 @@ function testContext({ appMeta = {}, config = {}, configDirectory, connections =
20
20
  warn: ()=>{}
21
21
  }, operators = {
22
22
  _test: ()=>'test'
23
- }, readConfigFile, secrets = {}, session } = {}) {
23
+ }, readConfigFile, secrets = {}, session, system } = {}) {
24
24
  return {
25
25
  appMeta,
26
26
  authorize: createAuthorize({
27
- session
27
+ session,
28
+ system
28
29
  }),
29
30
  config,
30
31
  configDirectory,
@@ -43,6 +44,7 @@ function testContext({ appMeta = {}, config = {}, configDirectory, connections =
43
44
  secrets,
44
45
  session,
45
46
  steps: {},
47
+ system,
46
48
  user: session?.user
47
49
  };
48
50
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/api",
3
- "version": "5.6.0",
3
+ "version": "6.0.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "",
6
6
  "homepage": "https://lowdefy.com",
@@ -34,18 +34,20 @@
34
34
  "dist/*"
35
35
  ],
36
36
  "dependencies": {
37
- "@lowdefy/ajv": "5.6.0",
38
- "@lowdefy/errors": "5.6.0",
39
- "@lowdefy/helpers": "5.6.0",
40
- "@lowdefy/node-utils": "5.6.0",
41
- "@lowdefy/nunjucks": "5.6.0",
42
- "@lowdefy/operators": "5.6.0",
43
- "@lowdefy/operators-js": "5.6.0"
37
+ "@lowdefy/ajv": "6.0.0",
38
+ "@lowdefy/build": "6.0.0",
39
+ "@lowdefy/errors": "6.0.0",
40
+ "@lowdefy/helpers": "6.0.0",
41
+ "@lowdefy/node-utils": "6.0.0",
42
+ "@lowdefy/nunjucks": "6.0.0",
43
+ "@lowdefy/operators": "6.0.0",
44
+ "@lowdefy/operators-js": "6.0.0",
45
+ "@modelcontextprotocol/sdk": "1.29.0"
44
46
  },
45
47
  "devDependencies": {
46
48
  "@jest/globals": "28.1.3",
47
- "@swc/cli": "0.8.0",
48
- "@swc/core": "1.15.18",
49
+ "@swc/cli": "0.8.1",
50
+ "@swc/core": "1.15.32",
49
51
  "@swc/jest": "0.2.39",
50
52
  "jest": "28.1.3"
51
53
  },