@lowdefy/build 5.2.0 → 5.3.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/dist/build/buildAgents.js +249 -0
- package/dist/build/buildImports/buildImportsDev.js +5 -0
- package/dist/build/buildImports/buildImportsProd.js +1 -0
- package/dist/build/buildTypes.js +7 -0
- package/dist/build/copyAgentFileSystems.js +45 -0
- package/dist/build/full/updateServerPackageJson.js +1 -0
- package/dist/build/jit/shallowBuild.js +15 -0
- package/dist/build/registerModules.js +5 -0
- package/dist/build/writeAgents.js +26 -0
- package/dist/build/writePluginImports/writeAgentImports.js +22 -0
- package/dist/build/writePluginImports/writePluginImports.js +5 -0
- package/dist/createContext.js +2 -0
- package/dist/defaultPackages.js +7 -0
- package/dist/defaultTypesMap.js +427 -357
- package/dist/index.js +15 -0
- package/dist/lowdefySchema.js +244 -0
- package/dist/scripts/generateDefaultTypes.js +1 -0
- package/dist/test-utils/testContext.js +2 -0
- package/dist/utils/createPluginTypesMap.js +7 -0
- package/package.json +50 -43
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/* eslint-disable no-param-reassign */ /*
|
|
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 path from 'path';
|
|
16
|
+
import fs from 'fs';
|
|
17
|
+
import { type } from '@lowdefy/helpers';
|
|
18
|
+
import { ConfigError, ConfigWarning } from '@lowdefy/errors';
|
|
19
|
+
import { RESERVED_PLATFORM_TOOL_NAMES } from '@lowdefy/ai-utils';
|
|
20
|
+
import countOperators from '../utils/countOperators.js';
|
|
21
|
+
import createCheckDuplicateId from '../utils/createCheckDuplicateId.js';
|
|
22
|
+
function detectCycles(agents) {
|
|
23
|
+
const graph = {};
|
|
24
|
+
for (const agent of agents){
|
|
25
|
+
graph[agent.agentId] = (agent.agents ?? []).map((ref)=>ref.agentId);
|
|
26
|
+
}
|
|
27
|
+
const visited = new Set();
|
|
28
|
+
const inStack = new Set();
|
|
29
|
+
function dfs(id) {
|
|
30
|
+
if (inStack.has(id)) return id;
|
|
31
|
+
if (visited.has(id)) return null;
|
|
32
|
+
visited.add(id);
|
|
33
|
+
inStack.add(id);
|
|
34
|
+
for (const neighbor of graph[id] ?? []){
|
|
35
|
+
const cycleNode = dfs(neighbor);
|
|
36
|
+
if (cycleNode !== null) return cycleNode;
|
|
37
|
+
}
|
|
38
|
+
inStack.delete(id);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
for (const id of Object.keys(graph)){
|
|
42
|
+
const cycleNode = dfs(id);
|
|
43
|
+
if (cycleNode !== null) {
|
|
44
|
+
return cycleNode;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
function buildAgents({ components, context }) {
|
|
50
|
+
if (!type.isArray(components.agents)) {
|
|
51
|
+
return components;
|
|
52
|
+
}
|
|
53
|
+
context.agentIds = new Set();
|
|
54
|
+
const checkDuplicateAgentId = createCheckDuplicateId({
|
|
55
|
+
message: 'Duplicate agentId "{{ id }}".'
|
|
56
|
+
});
|
|
57
|
+
components.agents.forEach((agent)=>{
|
|
58
|
+
const configKey = agent['~k'];
|
|
59
|
+
// Check duplicates
|
|
60
|
+
checkDuplicateAgentId({
|
|
61
|
+
id: agent.id,
|
|
62
|
+
configKey
|
|
63
|
+
});
|
|
64
|
+
// Track type usage for buildTypes validation
|
|
65
|
+
context.typeCounters.agents.increment(agent.type, configKey);
|
|
66
|
+
// Validate connectionId is provided
|
|
67
|
+
if (type.isNone(agent.connectionId)) {
|
|
68
|
+
throw new ConfigError(`Agent connectionId is not defined at "${agent.id}".`, {
|
|
69
|
+
configKey
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// Validate connectionId references an existing connection
|
|
73
|
+
// Connections may have been renamed by buildConnections:
|
|
74
|
+
// connection.connectionId = original id, connection.id = 'connection:' + original id
|
|
75
|
+
const connectionExists = (components.connections ?? []).some((c)=>c.id === agent.connectionId || c.connectionId === agent.connectionId);
|
|
76
|
+
if (!connectionExists) {
|
|
77
|
+
throw new ConfigError(`Agent "${agent.id}" references connectionId "${agent.connectionId}" which does not exist.`, {
|
|
78
|
+
configKey
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// Validate model is defined
|
|
82
|
+
if (type.isNone(agent.properties?.model)) {
|
|
83
|
+
throw new ConfigError(`Agent "model" is not defined at "${agent.id}".`, {
|
|
84
|
+
configKey
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
// Normalize tool strings to objects
|
|
88
|
+
agent.tools = (agent.tools ?? []).map((tool)=>{
|
|
89
|
+
if (type.isString(tool)) {
|
|
90
|
+
return {
|
|
91
|
+
endpointId: tool
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return tool;
|
|
95
|
+
});
|
|
96
|
+
// Validate tools reference existing API endpoints with required tool metadata
|
|
97
|
+
agent.tools.forEach((toolConfig)=>{
|
|
98
|
+
if (RESERVED_PLATFORM_TOOL_NAMES.includes(toolConfig.endpointId)) {
|
|
99
|
+
throw new ConfigError(`Agent "${agent.id}" tool "${toolConfig.endpointId}" uses a reserved platform tool name. Reserved: ${RESERVED_PLATFORM_TOOL_NAMES.join(', ')}.`, {
|
|
100
|
+
configKey
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const endpoint = (components.api ?? []).find((e)=>e.id === toolConfig.endpointId || e.endpointId === toolConfig.endpointId);
|
|
104
|
+
if (!endpoint) {
|
|
105
|
+
throw new ConfigError(`Agent "${agent.id}" references tool endpoint "${toolConfig.endpointId}" which does not exist.`, {
|
|
106
|
+
configKey
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (type.isNone(endpoint.description)) {
|
|
110
|
+
throw new ConfigError(`Endpoint "${toolConfig.endpointId}" is used as an agent tool but does not have a "description".`, {
|
|
111
|
+
configKey: endpoint['~k']
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (type.isNone(endpoint.payloadSchema)) {
|
|
115
|
+
throw new ConfigError(`Endpoint "${toolConfig.endpointId}" is used as an agent tool but does not have a "payloadSchema".`, {
|
|
116
|
+
configKey: endpoint['~k']
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
// Normalize MCP string shorthand to connectionId objects (same pattern as tools)
|
|
121
|
+
agent.mcp = (agent.mcp ?? []).map((mcp)=>{
|
|
122
|
+
if (type.isString(mcp)) {
|
|
123
|
+
return {
|
|
124
|
+
connectionId: mcp
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return mcp;
|
|
128
|
+
});
|
|
129
|
+
// Validate MCP sources
|
|
130
|
+
agent.mcp.forEach((mcpSource, index)=>{
|
|
131
|
+
if (!type.isNone(mcpSource.connectionId)) {
|
|
132
|
+
// Validate connectionId references an existing connection
|
|
133
|
+
const mcpConnectionExists = (components.connections ?? []).some((c)=>c.id === mcpSource.connectionId || c.connectionId === mcpSource.connectionId);
|
|
134
|
+
if (!mcpConnectionExists) {
|
|
135
|
+
throw new ConfigError(`Agent "${agent.id}" "mcp" source at index ${index} references connection "${mcpSource.connectionId}" which does not exist.`, {
|
|
136
|
+
configKey
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
} else if (mcpSource.transport === 'stdio') {
|
|
140
|
+
if (type.isNone(mcpSource.command)) {
|
|
141
|
+
throw new ConfigError(`Agent "${agent.id}" "mcp" source at index ${index} uses stdio transport but is missing "command".`, {
|
|
142
|
+
configKey
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
if (type.isNone(mcpSource.url)) {
|
|
147
|
+
throw new ConfigError(`Agent "${agent.id}" "mcp" source at index ${index} is missing "url".`, {
|
|
148
|
+
configKey
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
// Validate hooks reference existing API endpoints
|
|
154
|
+
const hookNames = [
|
|
155
|
+
'onStart',
|
|
156
|
+
'onStepStart',
|
|
157
|
+
'onToolCallStart',
|
|
158
|
+
'onToolCallFinish',
|
|
159
|
+
'onStepFinish',
|
|
160
|
+
'onFinish'
|
|
161
|
+
];
|
|
162
|
+
hookNames.forEach((hookName)=>{
|
|
163
|
+
(agent.hooks?.[hookName] ?? []).forEach((endpointId)=>{
|
|
164
|
+
const endpoint = (components.api ?? []).find((e)=>e.id === endpointId || e.endpointId === endpointId);
|
|
165
|
+
if (!endpoint) {
|
|
166
|
+
throw new ConfigError(`Agent "${agent.id}" hook "${hookName}" references endpoint "${endpointId}" which does not exist.`, {
|
|
167
|
+
configKey
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
// Normalize sub-agent strings to objects (same pattern as tools/mcp)
|
|
173
|
+
agent.agents = (agent.agents ?? []).map((ref)=>{
|
|
174
|
+
if (type.isString(ref)) {
|
|
175
|
+
return {
|
|
176
|
+
agentId: ref
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return ref;
|
|
180
|
+
});
|
|
181
|
+
// Validate fileSystem basePath if present
|
|
182
|
+
if (agent.properties?.fileSystem) {
|
|
183
|
+
const basePath = agent.properties.fileSystem.basePath;
|
|
184
|
+
if (!type.isString(basePath)) {
|
|
185
|
+
throw new ConfigError(`Agent "${agent.id}" fileSystem.basePath is not a string.`, {
|
|
186
|
+
received: basePath,
|
|
187
|
+
configKey
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
const resolved = path.resolve(context.directories.config, basePath);
|
|
191
|
+
if (!fs.existsSync(resolved)) {
|
|
192
|
+
throw new ConfigError(`Agent "${agent.id}" fileSystem.basePath "${basePath}" does not exist.`, {
|
|
193
|
+
configKey
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Rename id to internal format
|
|
198
|
+
agent.agentId = agent.id;
|
|
199
|
+
context.agentIds.add(agent.agentId);
|
|
200
|
+
agent.id = `agent:${agent.agentId}`;
|
|
201
|
+
// Count server operators in properties
|
|
202
|
+
countOperators(agent.properties ?? {}, {
|
|
203
|
+
counter: context.typeCounters.operators.server
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
// Second pass: validate sub-agent references (needs all agentIds collected)
|
|
207
|
+
components.agents.forEach((agent)=>{
|
|
208
|
+
const configKey = agent['~k'];
|
|
209
|
+
agent.agents.forEach((subAgentRef)=>{
|
|
210
|
+
// Validate sub-agent reference exists
|
|
211
|
+
if (!context.agentIds.has(subAgentRef.agentId)) {
|
|
212
|
+
throw new ConfigError(`Agent "${agent.agentId}" references sub-agent "${subAgentRef.agentId}" which does not exist.`, {
|
|
213
|
+
configKey
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
// Reserved platform tool name guard for sub-agents
|
|
217
|
+
if (RESERVED_PLATFORM_TOOL_NAMES.includes(subAgentRef.agentId)) {
|
|
218
|
+
throw new ConfigError(`Agent "${agent.agentId}" sub-agent "${subAgentRef.agentId}" uses a reserved platform tool name. Reserved: ${RESERVED_PLATFORM_TOOL_NAMES.join(', ')}.`, {
|
|
219
|
+
configKey
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
// Check for name collision with endpoint tools
|
|
223
|
+
const hasToolCollision = agent.tools.some((toolConfig)=>toolConfig.endpointId === subAgentRef.agentId);
|
|
224
|
+
if (hasToolCollision) {
|
|
225
|
+
throw new ConfigError(`Agent "${agent.agentId}" sub-agent "${subAgentRef.agentId}" conflicts with an endpoint tool of the same name.`, {
|
|
226
|
+
configKey
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
// Warn if sub-agent has tools with confirm: true (unsupported in sub-agent context)
|
|
230
|
+
const subAgent = components.agents.find((a)=>a.agentId === subAgentRef.agentId);
|
|
231
|
+
const hasConfirmTools = (subAgent?.tools ?? []).some((t)=>t.confirm);
|
|
232
|
+
if (hasConfirmTools) {
|
|
233
|
+
context.handleWarning(new ConfigWarning(`Agent "${subAgentRef.agentId}" has tools with confirm: true, but tool approval is not supported in sub-agent context. Tools will auto-execute when called as a sub-agent.`, {
|
|
234
|
+
configKey
|
|
235
|
+
}));
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
// Detect circular sub-agent references
|
|
240
|
+
const cycleNode = detectCycles(components.agents);
|
|
241
|
+
if (cycleNode !== null) {
|
|
242
|
+
const agent = components.agents.find((a)=>a.agentId === cycleNode);
|
|
243
|
+
throw new ConfigError(`Circular sub-agent reference detected involving "${cycleNode}".`, {
|
|
244
|
+
configKey: agent?.['~k']
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return components;
|
|
248
|
+
}
|
|
249
|
+
export default buildAgents;
|
|
@@ -22,6 +22,7 @@ function getPluginPackages({ components }) {
|
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
getPackages(components.types.actions);
|
|
25
|
+
getPackages(components.types.agents);
|
|
25
26
|
getPackages(components.types.auth.adapters);
|
|
26
27
|
getPackages(components.types.auth.callbacks);
|
|
27
28
|
getPackages(components.types.auth.events);
|
|
@@ -53,6 +54,10 @@ function buildImportsDev({ components, context }) {
|
|
|
53
54
|
pluginPackages,
|
|
54
55
|
map: context.typesMap.actions
|
|
55
56
|
}),
|
|
57
|
+
agents: buildImportClassDev({
|
|
58
|
+
pluginPackages,
|
|
59
|
+
map: context.typesMap.agents
|
|
60
|
+
}),
|
|
56
61
|
auth: {
|
|
57
62
|
adapters: buildImportClassDev({
|
|
58
63
|
pluginPackages,
|
|
@@ -25,6 +25,7 @@ function buildImportsProd({ components, context }) {
|
|
|
25
25
|
const blocks = buildImportClassProd(components.types.blocks);
|
|
26
26
|
return {
|
|
27
27
|
actions: buildImportClassProd(components.types.actions),
|
|
28
|
+
agents: buildImportClassProd(components.types.agents),
|
|
28
29
|
auth: {
|
|
29
30
|
adapters: buildImportClassProd(components.types.auth.adapters),
|
|
30
31
|
callbacks: buildImportClassProd(components.types.auth.callbacks),
|
package/dist/build/buildTypes.js
CHANGED
|
@@ -65,6 +65,7 @@ function buildTypes({ components, context }) {
|
|
|
65
65
|
typeCounters.actions.increment('SetDarkMode');
|
|
66
66
|
components.types = {
|
|
67
67
|
actions: {},
|
|
68
|
+
agents: {},
|
|
68
69
|
auth: {
|
|
69
70
|
adapters: {},
|
|
70
71
|
callbacks: {},
|
|
@@ -86,6 +87,12 @@ function buildTypes({ components, context }) {
|
|
|
86
87
|
store: components.types.actions,
|
|
87
88
|
typeClass: 'Action'
|
|
88
89
|
});
|
|
90
|
+
buildTypeClass(context, {
|
|
91
|
+
counter: typeCounters.agents,
|
|
92
|
+
definitions: context.typesMap.agents,
|
|
93
|
+
store: components.types.agents,
|
|
94
|
+
typeClass: 'Agent'
|
|
95
|
+
});
|
|
89
96
|
buildTypeClass(context, {
|
|
90
97
|
counter: typeCounters.auth.adapters,
|
|
91
98
|
definitions: context.typesMap.auth.adapters,
|
|
@@ -0,0 +1,45 @@
|
|
|
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 path from 'path';
|
|
16
|
+
import fs from 'fs';
|
|
17
|
+
import { copyFileOrDirectory } from '@lowdefy/node-utils';
|
|
18
|
+
async function copyAgentFileSystems({ components, context }) {
|
|
19
|
+
const basePaths = [];
|
|
20
|
+
const seen = new Set();
|
|
21
|
+
for (const agent of components.agents ?? []){
|
|
22
|
+
const basePath = agent.properties?.fileSystem?.basePath;
|
|
23
|
+
if (!basePath || typeof basePath !== 'string') continue;
|
|
24
|
+
if (seen.has(basePath)) continue;
|
|
25
|
+
seen.add(basePath);
|
|
26
|
+
basePaths.push(basePath);
|
|
27
|
+
}
|
|
28
|
+
// Manifest is consumed by next.config.js to populate outputFileTracingIncludes,
|
|
29
|
+
// so the Next.js tracer bundles these directories on Vercel and standalone builds.
|
|
30
|
+
await context.writeBuildArtifact('agentFileSystems.json', JSON.stringify(basePaths));
|
|
31
|
+
if (context.directories.config === context.directories.server) return;
|
|
32
|
+
for (const basePath of basePaths){
|
|
33
|
+
const source = path.resolve(context.directories.config, basePath);
|
|
34
|
+
if (!fs.existsSync(source)) continue;
|
|
35
|
+
const dest = path.resolve(context.directories.server, basePath);
|
|
36
|
+
try {
|
|
37
|
+
await copyFileOrDirectory(source, dest);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
throw new Error(`Failed to copy fileSystem basePath "${basePath}" to server directory: ${err.message}`, {
|
|
40
|
+
cause: err
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export default copyAgentFileSystems;
|
|
@@ -25,6 +25,7 @@ async function updateServerPackageJson({ components, context }) {
|
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
getPackages(components.types.actions);
|
|
28
|
+
getPackages(components.types.agents);
|
|
28
29
|
getPackages(components.types.auth.adapters);
|
|
29
30
|
getPackages(components.types.auth.callbacks);
|
|
30
31
|
getPackages(components.types.auth.events);
|
|
@@ -23,6 +23,7 @@ import addKeys from '../addKeys.js';
|
|
|
23
23
|
import buildApp from '../buildApp.js';
|
|
24
24
|
import buildAuth from '../buildAuth/buildAuth.js';
|
|
25
25
|
import buildConnections from '../buildConnections.js';
|
|
26
|
+
import buildAgents from '../buildAgents.js';
|
|
26
27
|
import buildApi from '../buildApi/buildApi.js';
|
|
27
28
|
import buildLogger from '../buildLogger.js';
|
|
28
29
|
import buildImports from '../buildImports/buildImports.js';
|
|
@@ -32,6 +33,7 @@ import buildModules from '../buildModules.js';
|
|
|
32
33
|
import buildRefs from '../buildRefs/buildRefs.js';
|
|
33
34
|
import buildTypes from '../buildTypes.js';
|
|
34
35
|
import cleanBuildDirectory from '../cleanBuildDirectory.js';
|
|
36
|
+
import copyAgentFileSystems from '../copyAgentFileSystems.js';
|
|
35
37
|
import copyPublicFolder from '../copyPublicFolder.js';
|
|
36
38
|
import testSchema from '../testSchema.js';
|
|
37
39
|
import validateConfig from '../validateConfig.js';
|
|
@@ -39,6 +41,7 @@ import writeApp from '../writeApp.js';
|
|
|
39
41
|
import writeAuth from '../writeAuth.js';
|
|
40
42
|
import writeConfig from '../writeConfig.js';
|
|
41
43
|
import writeConnections from '../writeConnections.js';
|
|
44
|
+
import writeAgents from '../writeAgents.js';
|
|
42
45
|
import writeApi from '../writeApi.js';
|
|
43
46
|
import writeGlobal from '../writeGlobal.js';
|
|
44
47
|
import writeJs from '../buildJs/writeJs.js';
|
|
@@ -146,6 +149,10 @@ async function shallowBuild(options) {
|
|
|
146
149
|
components,
|
|
147
150
|
context
|
|
148
151
|
});
|
|
152
|
+
tryBuildStep(buildAgents, 'buildAgents', {
|
|
153
|
+
components,
|
|
154
|
+
context
|
|
155
|
+
});
|
|
149
156
|
const { pageRegistry, sourcelessPageArtifacts } = buildShallowPages({
|
|
150
157
|
components,
|
|
151
158
|
context
|
|
@@ -207,6 +214,10 @@ async function shallowBuild(options) {
|
|
|
207
214
|
components,
|
|
208
215
|
context
|
|
209
216
|
});
|
|
217
|
+
await writeAgents({
|
|
218
|
+
components,
|
|
219
|
+
context
|
|
220
|
+
});
|
|
210
221
|
await writeConfig({
|
|
211
222
|
components,
|
|
212
223
|
context
|
|
@@ -265,6 +276,10 @@ async function shallowBuild(options) {
|
|
|
265
276
|
components,
|
|
266
277
|
context
|
|
267
278
|
});
|
|
279
|
+
await copyAgentFileSystems({
|
|
280
|
+
components,
|
|
281
|
+
context
|
|
282
|
+
});
|
|
268
283
|
return {
|
|
269
284
|
components,
|
|
270
285
|
pageRegistry,
|
|
@@ -156,6 +156,11 @@ async function resolveLocalManifest({ entry, resolvedPaths, context }) {
|
|
|
156
156
|
validateRequiredVars(varDefs, entry.vars ?? {}, entry.id, entry.source);
|
|
157
157
|
// Validate plugin dependencies against app's declared plugins
|
|
158
158
|
const requiredPlugins = manifest.plugins ?? [];
|
|
159
|
+
for (const plugin of requiredPlugins){
|
|
160
|
+
if (!type.isString(plugin.version)) {
|
|
161
|
+
throw new ConfigError(`Module "${entry.id}": plugin "${plugin.name}" must declare a "version" ` + `(semver range) in module.lowdefy.yaml.`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
159
164
|
const appPlugins = (context.plugins ?? []).reduce((map, p)=>map.set(p.name, p.version), new Map());
|
|
160
165
|
for (const plugin of requiredPlugins){
|
|
161
166
|
if (context.defaultPackageNames.has(plugin.name)) {
|
|
@@ -0,0 +1,26 @@
|
|
|
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, serializer } from '@lowdefy/helpers';
|
|
16
|
+
async function writeAgents({ components, context }) {
|
|
17
|
+
if (type.isNone(components.agents)) return;
|
|
18
|
+
if (!type.isArray(components.agents)) {
|
|
19
|
+
throw new Error(`Agents is not an array.`);
|
|
20
|
+
}
|
|
21
|
+
const writePromises = components.agents.map(async (agent)=>{
|
|
22
|
+
await context.writeBuildArtifact(`agents/${agent.agentId}.json`, serializer.serializeToString(agent));
|
|
23
|
+
});
|
|
24
|
+
return Promise.all(writePromises);
|
|
25
|
+
}
|
|
26
|
+
export default writeAgents;
|
|
@@ -0,0 +1,22 @@
|
|
|
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 generateImportFile from './generateImportFile.js';
|
|
16
|
+
async function writeAgentImports({ components, context }) {
|
|
17
|
+
await context.writeBuildArtifact('plugins/agents.js', generateImportFile({
|
|
18
|
+
imports: components.imports.agents,
|
|
19
|
+
importPath: 'agents'
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
export default writeAgentImports;
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import writeActionImports from './writeActionImports.js';
|
|
16
16
|
import writeActionSchemaMap from './writeActionSchemaMap.js';
|
|
17
|
+
import writeAgentImports from './writeAgentImports.js';
|
|
17
18
|
import writeAuthImports from './writeAuthImports.js';
|
|
18
19
|
import writeBlockImports from './writeBlockImports.js';
|
|
19
20
|
import writeBlockSchemaMap from './writeBlockSchemaMap.js';
|
|
@@ -31,6 +32,10 @@ async function writePluginImports({ components, context }) {
|
|
|
31
32
|
components,
|
|
32
33
|
context
|
|
33
34
|
});
|
|
35
|
+
await writeAgentImports({
|
|
36
|
+
components,
|
|
37
|
+
context
|
|
38
|
+
});
|
|
34
39
|
await writeAuthImports({
|
|
35
40
|
components,
|
|
36
41
|
context
|
package/dist/createContext.js
CHANGED
|
@@ -23,6 +23,7 @@ import defaultTypesMap from './defaultTypesMap.js';
|
|
|
23
23
|
function createContext({ customTypesMap, directories, logger, refResolver, stage = 'prod' }) {
|
|
24
24
|
const context = {
|
|
25
25
|
defaultPackageNames: new Set(defaultPackages),
|
|
26
|
+
agentIds: new Set(),
|
|
26
27
|
connectionIds: new Set(),
|
|
27
28
|
directories,
|
|
28
29
|
errors: [],
|
|
@@ -42,6 +43,7 @@ function createContext({ customTypesMap, directories, logger, refResolver, stage
|
|
|
42
43
|
stage,
|
|
43
44
|
typeCounters: {
|
|
44
45
|
actions: createCounter(),
|
|
46
|
+
agents: createCounter(),
|
|
45
47
|
auth: {
|
|
46
48
|
adapters: createCounter(),
|
|
47
49
|
callbacks: createCounter(),
|
package/dist/defaultPackages.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
'@lowdefy/actions-pdf-make',
|
|
18
18
|
'@lowdefy/blocks-aggrid',
|
|
19
19
|
'@lowdefy/blocks-antd',
|
|
20
|
+
'@lowdefy/blocks-antd-x',
|
|
20
21
|
'@lowdefy/blocks-basic',
|
|
21
22
|
'@lowdefy/blocks-diff',
|
|
22
23
|
'@lowdefy/blocks-echarts',
|
|
@@ -25,7 +26,13 @@
|
|
|
25
26
|
'@lowdefy/blocks-markdown',
|
|
26
27
|
'@lowdefy/blocks-qr',
|
|
27
28
|
'@lowdefy/blocks-tiptap',
|
|
29
|
+
'@lowdefy/ai-utils',
|
|
30
|
+
'@lowdefy/connection-ai-gateway',
|
|
31
|
+
'@lowdefy/connection-anthropic',
|
|
28
32
|
'@lowdefy/connection-axios-http',
|
|
33
|
+
'@lowdefy/connection-google',
|
|
34
|
+
'@lowdefy/connection-mcp',
|
|
35
|
+
'@lowdefy/connection-openai',
|
|
29
36
|
'@lowdefy/connection-elasticsearch',
|
|
30
37
|
'@lowdefy/connection-test',
|
|
31
38
|
'@lowdefy/connection-google-sheets',
|