@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.
- package/dist/context/createAuthorize.js +4 -1
- package/dist/context/resolveStrategyCaller.js +56 -0
- package/dist/index.js +12 -2
- package/dist/routes/agent/callAgent.js +7 -184
- package/dist/routes/agent/prepareAgent.js +173 -0
- package/dist/routes/auth/createLogger.js +2 -5
- package/dist/routes/auth/createPrefixedCookies.js +52 -0
- package/dist/routes/auth/{getNextAuthConfig.js → getAuthConfig.js} +27 -16
- package/dist/routes/auth/resolveCookies.js +37 -0
- package/dist/routes/auth/strategies/createAuthStrategies.js +77 -0
- package/dist/routes/auth/strategies/getAuthStrategies.js +31 -0
- package/dist/routes/endpoints/authorizeApiEndpoint.js +8 -2
- package/dist/routes/endpoints/callEndpoint.js +26 -3
- package/dist/routes/endpoints/control/controlReject.js +3 -1
- package/dist/routes/endpoints/control/controlThrow.js +4 -1
- package/dist/routes/endpoints/findSchedule.js +35 -0
- package/dist/routes/endpoints/forwardScheduledEndpoint.js +106 -0
- package/dist/routes/endpoints/getEndpointConfig.js +9 -3
- package/dist/routes/endpoints/getEnvironmentSchedules.js +29 -0
- package/dist/routes/endpoints/handleAgentCall.js +83 -0
- package/dist/routes/endpoints/handleEndpointCall.js +49 -1
- package/dist/routes/endpoints/handleRenderNotification.js +189 -0
- package/dist/routes/endpoints/handleValidateSchema.js +3 -1
- package/dist/routes/endpoints/isUnauthenticatedHuman.js +31 -0
- package/dist/routes/endpoints/resolveCronEnvironment.js +37 -0
- package/dist/routes/endpoints/runDetachedEndpoint.js +66 -0
- package/dist/routes/endpoints/runRoutine.js +12 -0
- package/dist/routes/endpoints/runScheduledEndpoint.js +104 -0
- package/dist/routes/endpoints/runWebhookEndpoint.js +83 -0
- package/dist/routes/endpoints/scheduleBackground.js +48 -0
- package/dist/routes/mcp/createMcpServer.js +160 -0
- package/dist/routes/notifications/derivePreview.js +30 -0
- package/dist/routes/notifications/getNotificationConfig.js +32 -0
- package/dist/routes/notifications/resolveNotificationLinks.js +72 -0
- package/dist/routes/notifications/resolveThemeLogo.js +35 -0
- package/dist/routes/page/dynamic/resolveDynamicContent.js +172 -0
- package/dist/routes/page/dynamic/unescapeOperators.js +36 -0
- package/dist/routes/page/dynamic/validateFragment.js +104 -0
- package/dist/routes/page/getPageConfig.js +16 -6
- package/dist/routes/websocket/authorizeWebsocket.js +27 -0
- package/dist/routes/websocket/createChannelRegistry.js +269 -0
- package/dist/routes/websocket/createWebSocketConnection.js +131 -0
- package/dist/routes/websocket/getWebsocketConfig.js +30 -0
- package/dist/routes/websocket/getWebsocketResolver.js +33 -0
- package/dist/routes/websocket/prepareChannel.js +72 -0
- package/dist/test/testContext.js +4 -2
- package/package.json +12 -10
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import { ConfigError } from '@lowdefy/errors';
|
|
16
|
-
function createAuthorize({ session }) {
|
|
16
|
+
function createAuthorize({ session, system = false }) {
|
|
17
17
|
// Next-auth getSession provides a session object if the user is authenticated
|
|
18
18
|
// else session will be null
|
|
19
19
|
const authenticated = !!session;
|
|
@@ -24,6 +24,9 @@ function createAuthorize({ session }) {
|
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
function authorize(config) {
|
|
27
|
+
// A system context (scheduled, webhook, detached runs) was authorized at the
|
|
28
|
+
// transport layer, so nested endpoint calls are never gated on a user session.
|
|
29
|
+
if (system === true) return true;
|
|
27
30
|
const { auth } = config;
|
|
28
31
|
if (auth.public === true) return true;
|
|
29
32
|
if (auth.public === false) {
|
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
// Tries the configured API auth strategies in config order; the first
|
|
17
|
+
// verifier match wins. A strategy caller is a config-derived, org-less
|
|
18
|
+
// principal: roles are the strategy's static grant unioned with any
|
|
19
|
+
// claim-derived roles, and attributes are the static bag shallow-merged
|
|
20
|
+
// with claim-mapped values (claim wins), so _user.attributes reads the same
|
|
21
|
+
// for session and strategy callers.
|
|
22
|
+
async function resolveStrategyCaller({ headers, logger, strategies }) {
|
|
23
|
+
for (const strategy of strategies ?? []){
|
|
24
|
+
const match = await strategy.verify({
|
|
25
|
+
headers,
|
|
26
|
+
logger
|
|
27
|
+
});
|
|
28
|
+
if (type.isNone(match)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const roles = [
|
|
32
|
+
...new Set([
|
|
33
|
+
...strategy.roles,
|
|
34
|
+
...match.roles ?? []
|
|
35
|
+
])
|
|
36
|
+
];
|
|
37
|
+
const attributes = {
|
|
38
|
+
...strategy.attributes,
|
|
39
|
+
...match.attributes ?? {}
|
|
40
|
+
};
|
|
41
|
+
logger.debug({
|
|
42
|
+
event: 'auth_strategy_authenticated',
|
|
43
|
+
authMethod: strategy.type,
|
|
44
|
+
strategyId: strategy.id
|
|
45
|
+
}, `Request authenticated by auth strategy "${strategy.id}" (${strategy.type}).`);
|
|
46
|
+
return {
|
|
47
|
+
...match.user,
|
|
48
|
+
authMethod: strategy.type,
|
|
49
|
+
strategyId: strategy.id,
|
|
50
|
+
roles,
|
|
51
|
+
attributes
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
export default resolveStrategyCaller;
|
package/dist/index.js
CHANGED
|
@@ -15,14 +15,24 @@
|
|
|
15
15
|
*/ import buildEndpointResult from './response/buildEndpointResult.js';
|
|
16
16
|
import callAgent from './routes/agent/callAgent.js';
|
|
17
17
|
import callEndpoint from './routes/endpoints/callEndpoint.js';
|
|
18
|
+
import getEndpointConfig from './routes/endpoints/getEndpointConfig.js';
|
|
18
19
|
import callRequest from './routes/request/callRequest.js';
|
|
19
20
|
import createApiContext from './context/createApiContext.js';
|
|
21
|
+
import createChannelRegistry from './routes/websocket/createChannelRegistry.js';
|
|
22
|
+
import createMcpServer from './routes/mcp/createMcpServer.js';
|
|
23
|
+
import createWebSocketConnection from './routes/websocket/createWebSocketConnection.js';
|
|
20
24
|
import createSessionCallback from './routes/auth/callbacks/createSessionCallback.js';
|
|
25
|
+
import getAuthConfig from './routes/auth/getAuthConfig.js';
|
|
26
|
+
import getAuthStrategies from './routes/auth/strategies/getAuthStrategies.js';
|
|
21
27
|
import getHomeAndMenus from './routes/rootConfig/getHomeAndMenus.js';
|
|
22
|
-
import
|
|
28
|
+
import resolveStrategyCaller from './context/resolveStrategyCaller.js';
|
|
23
29
|
import getPageConfig from './routes/page/getPageConfig.js';
|
|
24
30
|
import getRootConfig from './routes/rootConfig/getRootConfig.js';
|
|
25
31
|
import logClientError from './routes/log/logClientError.js';
|
|
26
32
|
import redactErrorResponse from './response/redactErrorResponse.js';
|
|
27
33
|
import redactResponse from './response/redactResponse.js';
|
|
28
|
-
|
|
34
|
+
import forwardScheduledEndpoint from './routes/endpoints/forwardScheduledEndpoint.js';
|
|
35
|
+
import runDetachedEndpoint from './routes/endpoints/runDetachedEndpoint.js';
|
|
36
|
+
import runWebhookEndpoint from './routes/endpoints/runWebhookEndpoint.js';
|
|
37
|
+
import runScheduledEndpoint from './routes/endpoints/runScheduledEndpoint.js';
|
|
38
|
+
export { buildEndpointResult, callAgent, callEndpoint, getEndpointConfig, callRequest, createApiContext, createChannelRegistry, createMcpServer, createSessionCallback, createWebSocketConnection, getAuthConfig, getAuthStrategies, getHomeAndMenus, getPageConfig, getRootConfig, logClientError, redactErrorResponse, redactResponse, resolveStrategyCaller, forwardScheduledEndpoint, runDetachedEndpoint, runWebhookEndpoint, runScheduledEndpoint };
|
|
@@ -12,17 +12,8 @@
|
|
|
12
12
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
|
-
*/ import
|
|
16
|
-
import
|
|
17
|
-
import createEvaluateOperators from '../../context/createEvaluateOperators.js';
|
|
18
|
-
import authorizeAgent from './authorizeAgent.js';
|
|
19
|
-
import authorizeApiEndpoint from '../endpoints/authorizeApiEndpoint.js';
|
|
20
|
-
import getEndpointConfig from '../endpoints/getEndpointConfig.js';
|
|
21
|
-
import runRoutine from '../endpoints/runRoutine.js';
|
|
22
|
-
import getAgentConfig from './getAgentConfig.js';
|
|
23
|
-
import getAgentResolver from './getAgentResolver.js';
|
|
24
|
-
import getConnectionConfig from '../connections/getConnectionConfig.js';
|
|
25
|
-
import getConnection from '../connections/getConnection.js';
|
|
15
|
+
*/ import createEvaluateOperators from '../../context/createEvaluateOperators.js';
|
|
16
|
+
import prepareAgent from './prepareAgent.js';
|
|
26
17
|
async function callAgent(context, { agentId, pageId, messages, conversationId, urlQuery, sharedState }) {
|
|
27
18
|
const { logger } = context;
|
|
28
19
|
context.pageId = pageId;
|
|
@@ -32,12 +23,6 @@ async function callAgent(context, { agentId, pageId, messages, conversationId, u
|
|
|
32
23
|
agentId,
|
|
33
24
|
pageId
|
|
34
25
|
});
|
|
35
|
-
const agentConfig = await getAgentConfig(context, {
|
|
36
|
-
agentId
|
|
37
|
-
});
|
|
38
|
-
authorizeAgent(context, {
|
|
39
|
-
agentConfig
|
|
40
|
-
});
|
|
41
26
|
const agentContext = {
|
|
42
27
|
conversationId: conversationId ?? undefined,
|
|
43
28
|
pageId,
|
|
@@ -45,174 +30,12 @@ async function callAgent(context, { agentId, pageId, messages, conversationId, u
|
|
|
45
30
|
urlQuery: urlQuery ?? {},
|
|
46
31
|
userId: context.user?.sub ?? context.user?.id ?? null
|
|
47
32
|
};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
input: agentConfig.properties ?? {},
|
|
51
|
-
location: agentConfig.agentId,
|
|
52
|
-
payload: agentContext,
|
|
53
|
-
state: {},
|
|
54
|
-
steps: {}
|
|
55
|
-
});
|
|
56
|
-
// Load connection config from build artifacts using agent's connectionId
|
|
57
|
-
const connectionConfig = await getConnectionConfig(context, {
|
|
58
|
-
connectionId: agentConfig.connectionId,
|
|
59
|
-
configKey: agentConfig['~k']
|
|
60
|
-
});
|
|
61
|
-
// Get connection plugin from registry
|
|
62
|
-
const connection = getConnection(context, {
|
|
63
|
-
connectionConfig
|
|
64
|
-
});
|
|
65
|
-
// Evaluate operators in connection properties
|
|
66
|
-
const connectionProperties = context.evaluateOperators({
|
|
67
|
-
input: connectionConfig.properties || {},
|
|
68
|
-
location: connectionConfig.connectionId,
|
|
69
|
-
payload: {},
|
|
70
|
-
state: {},
|
|
71
|
-
steps: {}
|
|
72
|
-
});
|
|
73
|
-
// Create connection instance (e.g., Anthropic provider)
|
|
74
|
-
const connectionInstance = connection.create({
|
|
75
|
-
connection: connectionProperties
|
|
76
|
-
});
|
|
77
|
-
// Get agent type from plugin registry
|
|
78
|
-
const agentType = getAgentResolver(context, {
|
|
79
|
-
agentConfig
|
|
80
|
-
});
|
|
81
|
-
// Build resolver context with callEndpoint that allows InternalApi endpoints
|
|
82
|
-
const resolverContext = {
|
|
33
|
+
const { agentConfig, connectionInstance, agentType, resolverContext } = await prepareAgent(context, {
|
|
34
|
+
agentId,
|
|
83
35
|
agentContext,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
location: agentConfig.agentId,
|
|
88
|
-
payload: agentContext,
|
|
89
|
-
state: {},
|
|
90
|
-
steps: {}
|
|
91
|
-
}),
|
|
92
|
-
callEndpoint: async (endpointId, { payload, abortSignal })=>{
|
|
93
|
-
const endpointConfig = await getEndpointConfig(context, {
|
|
94
|
-
endpointId
|
|
95
|
-
});
|
|
96
|
-
authorizeApiEndpoint(context, {
|
|
97
|
-
endpointConfig
|
|
98
|
-
});
|
|
99
|
-
const routineContext = {
|
|
100
|
-
steps: {},
|
|
101
|
-
payload: payload ?? {},
|
|
102
|
-
arrayIndices: [],
|
|
103
|
-
items: {},
|
|
104
|
-
state: {},
|
|
105
|
-
endpointDepth: 0
|
|
106
|
-
};
|
|
107
|
-
const { error, response, status } = await runRoutine(context, routineContext, {
|
|
108
|
-
routine: endpointConfig.routine
|
|
109
|
-
});
|
|
110
|
-
return buildEndpointResult(context, {
|
|
111
|
-
error,
|
|
112
|
-
response,
|
|
113
|
-
status
|
|
114
|
-
});
|
|
115
|
-
},
|
|
116
|
-
getEndpointConfig: async ({ endpointId })=>{
|
|
117
|
-
return getEndpointConfig(context, {
|
|
118
|
-
endpointId
|
|
119
|
-
});
|
|
120
|
-
},
|
|
121
|
-
getAgentConfig: async ({ agentId })=>{
|
|
122
|
-
const subAgentConfig = await getAgentConfig(context, {
|
|
123
|
-
agentId
|
|
124
|
-
});
|
|
125
|
-
authorizeAgent(context, {
|
|
126
|
-
agentConfig: subAgentConfig
|
|
127
|
-
});
|
|
128
|
-
return subAgentConfig;
|
|
129
|
-
},
|
|
130
|
-
getConnectionForAgent: async ({ agentConfig: subAgentConfig })=>{
|
|
131
|
-
const subConnectionConfig = await getConnectionConfig(context, {
|
|
132
|
-
connectionId: subAgentConfig.connectionId,
|
|
133
|
-
configKey: subAgentConfig['~k']
|
|
134
|
-
});
|
|
135
|
-
const subConnection = getConnection(context, {
|
|
136
|
-
connectionConfig: subConnectionConfig
|
|
137
|
-
});
|
|
138
|
-
const subConnectionProperties = context.evaluateOperators({
|
|
139
|
-
input: subConnectionConfig.properties || {},
|
|
140
|
-
location: subConnectionConfig.connectionId,
|
|
141
|
-
payload: {},
|
|
142
|
-
state: {},
|
|
143
|
-
steps: {}
|
|
144
|
-
});
|
|
145
|
-
return subConnection.create({
|
|
146
|
-
connection: subConnectionProperties
|
|
147
|
-
});
|
|
148
|
-
},
|
|
149
|
-
resolveMcpSources: async ({ agentConfig: subAgentConfig })=>{
|
|
150
|
-
const resolvedMcp = [];
|
|
151
|
-
for (const mcpSource of subAgentConfig.mcp ?? []){
|
|
152
|
-
if (!type.isNone(mcpSource.connectionId)) {
|
|
153
|
-
const mcpConnConfig = await getConnectionConfig(context, {
|
|
154
|
-
connectionId: mcpSource.connectionId,
|
|
155
|
-
configKey: subAgentConfig['~k']
|
|
156
|
-
});
|
|
157
|
-
const mcpConnection = getConnection(context, {
|
|
158
|
-
connectionConfig: mcpConnConfig
|
|
159
|
-
});
|
|
160
|
-
const mcpConnProps = context.evaluateOperators({
|
|
161
|
-
input: mcpConnConfig.properties || {},
|
|
162
|
-
location: mcpConnConfig.connectionId,
|
|
163
|
-
payload: {},
|
|
164
|
-
state: {},
|
|
165
|
-
steps: {}
|
|
166
|
-
});
|
|
167
|
-
const mcpConfig = mcpConnection.create({
|
|
168
|
-
connection: mcpConnProps
|
|
169
|
-
});
|
|
170
|
-
const { connectionId: _, ...overrides } = mcpSource;
|
|
171
|
-
resolvedMcp.push({
|
|
172
|
-
...mcpConfig,
|
|
173
|
-
...overrides
|
|
174
|
-
});
|
|
175
|
-
} else {
|
|
176
|
-
resolvedMcp.push(mcpSource);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
return resolvedMcp;
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
// Resolve MCP connection references to inline config.
|
|
183
|
-
// Agent-level overrides (like confirm) may still contain operators —
|
|
184
|
-
// handleAgentChat evaluates those via its existing evaluateOperators call.
|
|
185
|
-
const resolvedMcp = [];
|
|
186
|
-
for (const mcpSource of agentConfig.mcp ?? []){
|
|
187
|
-
if (!type.isNone(mcpSource.connectionId)) {
|
|
188
|
-
const mcpConnConfig = await getConnectionConfig(context, {
|
|
189
|
-
connectionId: mcpSource.connectionId,
|
|
190
|
-
configKey: agentConfig['~k']
|
|
191
|
-
});
|
|
192
|
-
const mcpConnection = getConnection(context, {
|
|
193
|
-
connectionConfig: mcpConnConfig
|
|
194
|
-
});
|
|
195
|
-
const mcpConnProps = context.evaluateOperators({
|
|
196
|
-
input: mcpConnConfig.properties || {},
|
|
197
|
-
location: mcpConnConfig.connectionId,
|
|
198
|
-
payload: {},
|
|
199
|
-
state: {},
|
|
200
|
-
steps: {}
|
|
201
|
-
});
|
|
202
|
-
const mcpConfig = mcpConnection.create({
|
|
203
|
-
connection: mcpConnProps
|
|
204
|
-
});
|
|
205
|
-
// Merge: connection properties as base, agent-level overrides on top
|
|
206
|
-
const { connectionId: _, ...overrides } = mcpSource;
|
|
207
|
-
resolvedMcp.push({
|
|
208
|
-
...mcpConfig,
|
|
209
|
-
...overrides
|
|
210
|
-
});
|
|
211
|
-
} else {
|
|
212
|
-
resolvedMcp.push(mcpSource);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
agentConfig.mcp = resolvedMcp;
|
|
36
|
+
endpointDepth: 0,
|
|
37
|
+
mode: 'chat'
|
|
38
|
+
});
|
|
216
39
|
// Call the agent resolver
|
|
217
40
|
const { response } = await agentType.resolver({
|
|
218
41
|
connection: connectionInstance,
|
|
@@ -0,0 +1,173 @@
|
|
|
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 buildEndpointResult from '../../response/buildEndpointResult.js';
|
|
17
|
+
import getEndpointConfig from '../endpoints/getEndpointConfig.js';
|
|
18
|
+
import invokeEndpoint from '../endpoints/invokeEndpoint.js';
|
|
19
|
+
import authorizeAgent from './authorizeAgent.js';
|
|
20
|
+
import getAgentConfig from './getAgentConfig.js';
|
|
21
|
+
import getAgentResolver from './getAgentResolver.js';
|
|
22
|
+
import getConnectionConfig from '../connections/getConnectionConfig.js';
|
|
23
|
+
import getConnection from '../connections/getConnection.js';
|
|
24
|
+
// Shared agent invocation construction for the streaming chat route (callAgent)
|
|
25
|
+
// and the headless CallAgent routine step (handleAgentCall): loads the agent
|
|
26
|
+
// config, evaluates operators, creates the provider connection, and builds the
|
|
27
|
+
// resolver context. mode ('chat' | 'generate') selects the resolver's execution
|
|
28
|
+
// path; endpointDepth threads the endpoint call depth cap through agent tool
|
|
29
|
+
// and hook endpoint calls.
|
|
30
|
+
async function prepareAgent(context, { agentId, agentContext, endpointDepth = 0, mode = 'chat' }) {
|
|
31
|
+
const agentConfig = await getAgentConfig(context, {
|
|
32
|
+
agentId
|
|
33
|
+
});
|
|
34
|
+
authorizeAgent(context, {
|
|
35
|
+
agentConfig
|
|
36
|
+
});
|
|
37
|
+
// Evaluate operators in agent properties (e.g. _user, _secret, _payload)
|
|
38
|
+
agentConfig.properties = context.evaluateOperators({
|
|
39
|
+
input: agentConfig.properties ?? {},
|
|
40
|
+
location: agentConfig.agentId,
|
|
41
|
+
payload: agentContext,
|
|
42
|
+
state: {},
|
|
43
|
+
steps: {}
|
|
44
|
+
});
|
|
45
|
+
// Load connection config from build artifacts using agent's connectionId
|
|
46
|
+
const connectionConfig = await getConnectionConfig(context, {
|
|
47
|
+
connectionId: agentConfig.connectionId,
|
|
48
|
+
configKey: agentConfig['~k']
|
|
49
|
+
});
|
|
50
|
+
// Get connection plugin from registry
|
|
51
|
+
const connection = getConnection(context, {
|
|
52
|
+
connectionConfig
|
|
53
|
+
});
|
|
54
|
+
// Evaluate operators in connection properties
|
|
55
|
+
const connectionProperties = context.evaluateOperators({
|
|
56
|
+
input: connectionConfig.properties || {},
|
|
57
|
+
location: connectionConfig.connectionId,
|
|
58
|
+
payload: {},
|
|
59
|
+
state: {},
|
|
60
|
+
steps: {}
|
|
61
|
+
});
|
|
62
|
+
// Create connection instance (e.g., Anthropic provider)
|
|
63
|
+
const connectionInstance = connection.create({
|
|
64
|
+
connection: connectionProperties
|
|
65
|
+
});
|
|
66
|
+
// Get agent type from plugin registry
|
|
67
|
+
const agentType = getAgentResolver(context, {
|
|
68
|
+
agentConfig
|
|
69
|
+
});
|
|
70
|
+
// Build resolver context with callEndpoint that allows InternalApi endpoints
|
|
71
|
+
const resolverContext = {
|
|
72
|
+
agentContext,
|
|
73
|
+
i18n: context.i18n,
|
|
74
|
+
mode,
|
|
75
|
+
evaluateOperators: (input)=>context.evaluateOperators({
|
|
76
|
+
input,
|
|
77
|
+
location: agentConfig.agentId,
|
|
78
|
+
payload: agentContext,
|
|
79
|
+
state: {},
|
|
80
|
+
steps: {}
|
|
81
|
+
}),
|
|
82
|
+
callEndpoint: async (endpointId, { payload })=>{
|
|
83
|
+
const { error, response, status } = await invokeEndpoint(context, {
|
|
84
|
+
endpointId,
|
|
85
|
+
payload,
|
|
86
|
+
endpointDepth
|
|
87
|
+
});
|
|
88
|
+
return buildEndpointResult(context, {
|
|
89
|
+
error,
|
|
90
|
+
response,
|
|
91
|
+
status
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
getEndpointConfig: async ({ endpointId })=>{
|
|
95
|
+
return getEndpointConfig(context, {
|
|
96
|
+
endpointId
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
getAgentConfig: async ({ agentId: subAgentId })=>{
|
|
100
|
+
const subAgentConfig = await getAgentConfig(context, {
|
|
101
|
+
agentId: subAgentId
|
|
102
|
+
});
|
|
103
|
+
authorizeAgent(context, {
|
|
104
|
+
agentConfig: subAgentConfig
|
|
105
|
+
});
|
|
106
|
+
return subAgentConfig;
|
|
107
|
+
},
|
|
108
|
+
getConnectionForAgent: async ({ agentConfig: subAgentConfig })=>{
|
|
109
|
+
const subConnectionConfig = await getConnectionConfig(context, {
|
|
110
|
+
connectionId: subAgentConfig.connectionId,
|
|
111
|
+
configKey: subAgentConfig['~k']
|
|
112
|
+
});
|
|
113
|
+
const subConnection = getConnection(context, {
|
|
114
|
+
connectionConfig: subConnectionConfig
|
|
115
|
+
});
|
|
116
|
+
const subConnectionProperties = context.evaluateOperators({
|
|
117
|
+
input: subConnectionConfig.properties || {},
|
|
118
|
+
location: subConnectionConfig.connectionId,
|
|
119
|
+
payload: {},
|
|
120
|
+
state: {},
|
|
121
|
+
steps: {}
|
|
122
|
+
});
|
|
123
|
+
return subConnection.create({
|
|
124
|
+
connection: subConnectionProperties
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
resolveMcpSources: async ({ agentConfig: subAgentConfig })=>{
|
|
128
|
+
const resolvedMcp = [];
|
|
129
|
+
for (const mcpSource of subAgentConfig.mcp ?? []){
|
|
130
|
+
if (!type.isNone(mcpSource.connectionId)) {
|
|
131
|
+
const mcpConnConfig = await getConnectionConfig(context, {
|
|
132
|
+
connectionId: mcpSource.connectionId,
|
|
133
|
+
configKey: subAgentConfig['~k']
|
|
134
|
+
});
|
|
135
|
+
const mcpConnection = getConnection(context, {
|
|
136
|
+
connectionConfig: mcpConnConfig
|
|
137
|
+
});
|
|
138
|
+
const mcpConnProps = context.evaluateOperators({
|
|
139
|
+
input: mcpConnConfig.properties || {},
|
|
140
|
+
location: mcpConnConfig.connectionId,
|
|
141
|
+
payload: {},
|
|
142
|
+
state: {},
|
|
143
|
+
steps: {}
|
|
144
|
+
});
|
|
145
|
+
const mcpConfig = mcpConnection.create({
|
|
146
|
+
connection: mcpConnProps
|
|
147
|
+
});
|
|
148
|
+
const { connectionId: _, ...overrides } = mcpSource;
|
|
149
|
+
resolvedMcp.push({
|
|
150
|
+
...mcpConfig,
|
|
151
|
+
...overrides
|
|
152
|
+
});
|
|
153
|
+
} else {
|
|
154
|
+
resolvedMcp.push(mcpSource);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return resolvedMcp;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
// Resolve MCP connection references to inline config.
|
|
161
|
+
// Agent-level overrides (like confirm) may still contain operators —
|
|
162
|
+
// handleAgentChat evaluates those via its existing evaluateOperators call.
|
|
163
|
+
agentConfig.mcp = await resolverContext.resolveMcpSources({
|
|
164
|
+
agentConfig
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
agentConfig,
|
|
168
|
+
connectionInstance,
|
|
169
|
+
agentType,
|
|
170
|
+
resolverContext
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
export default prepareAgent;
|
|
@@ -20,11 +20,8 @@
|
|
|
20
20
|
};
|
|
21
21
|
function createLogger({ logger }) {
|
|
22
22
|
return {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (error) {
|
|
26
|
-
error.code = code;
|
|
27
|
-
}
|
|
23
|
+
// Auth.js v5 logger contract: error(error), warn(code), debug(message, metadata).
|
|
24
|
+
error: (error)=>{
|
|
28
25
|
logger.error(error);
|
|
29
26
|
},
|
|
30
27
|
warn: (code)=>{
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
*/ function slugifyPrefix(value) {
|
|
16
|
+
return String(value).trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
17
|
+
}
|
|
18
|
+
// Injects an app-specific prefix into each Auth.js cookie name so multiple apps
|
|
19
|
+
// on the same host do not share a cookie jar (browsers do not scope cookies by
|
|
20
|
+
// port). Only names are overridden — Auth.js v5 deep-merges `config.cookies`
|
|
21
|
+
// into its defaults, so cookie options (httpOnly, secure, maxAge) stay in sync
|
|
22
|
+
// with @auth/core's per-request defaults. Names mirror defaultCookies in
|
|
23
|
+
// @auth/core/lib/utils/cookie.js.
|
|
24
|
+
function createPrefixedCookies({ prefix, useSecureCookies }) {
|
|
25
|
+
const securePrefix = useSecureCookies ? '__Secure-' : '';
|
|
26
|
+
const hostPrefix = useSecureCookies ? '__Host-' : '';
|
|
27
|
+
return {
|
|
28
|
+
sessionToken: {
|
|
29
|
+
name: `${securePrefix}${prefix}.authjs.session-token`
|
|
30
|
+
},
|
|
31
|
+
callbackUrl: {
|
|
32
|
+
name: `${securePrefix}${prefix}.authjs.callback-url`
|
|
33
|
+
},
|
|
34
|
+
csrfToken: {
|
|
35
|
+
name: `${hostPrefix}${prefix}.authjs.csrf-token`
|
|
36
|
+
},
|
|
37
|
+
pkceCodeVerifier: {
|
|
38
|
+
name: `${securePrefix}${prefix}.authjs.pkce.code_verifier`
|
|
39
|
+
},
|
|
40
|
+
state: {
|
|
41
|
+
name: `${securePrefix}${prefix}.authjs.state`
|
|
42
|
+
},
|
|
43
|
+
nonce: {
|
|
44
|
+
name: `${securePrefix}${prefix}.authjs.nonce`
|
|
45
|
+
},
|
|
46
|
+
webauthnChallenge: {
|
|
47
|
+
name: `${securePrefix}${prefix}.authjs.challenge`
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export { slugifyPrefix };
|
|
52
|
+
export default createPrefixedCookies;
|
|
@@ -19,10 +19,11 @@ import createCallbacks from './callbacks/createCallbacks.js';
|
|
|
19
19
|
import createEvents from './events/createEvents.js';
|
|
20
20
|
import createLogger from './createLogger.js';
|
|
21
21
|
import createProviders from './createProviders.js';
|
|
22
|
-
|
|
22
|
+
import resolveCookies from './resolveCookies.js';
|
|
23
|
+
const authConfigCache = {};
|
|
23
24
|
let initialized = false;
|
|
24
|
-
function
|
|
25
|
-
if (initialized) return
|
|
25
|
+
function getAuthConfig({ appMeta, authJson, dev, logger, plugins, secrets }) {
|
|
26
|
+
if (initialized) return authConfigCache;
|
|
26
27
|
const operatorsParser = new ServerParser({
|
|
27
28
|
lowdefyApp: appMeta,
|
|
28
29
|
operators: {
|
|
@@ -40,36 +41,46 @@ function getNextAuthConfig({ appMeta, authJson, logger, plugins, secrets }) {
|
|
|
40
41
|
if (operatorErrors.length > 0) {
|
|
41
42
|
throw operatorErrors[0];
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
+
authConfigCache.adapter = createAdapter({
|
|
44
45
|
authConfig,
|
|
45
46
|
logger,
|
|
46
47
|
plugins
|
|
47
48
|
});
|
|
48
|
-
|
|
49
|
+
authConfigCache.callbacks = createCallbacks({
|
|
49
50
|
authConfig,
|
|
50
51
|
logger,
|
|
51
52
|
plugins
|
|
52
53
|
});
|
|
53
|
-
|
|
54
|
+
authConfigCache.events = createEvents({
|
|
54
55
|
authConfig,
|
|
55
56
|
logger,
|
|
56
57
|
plugins
|
|
57
58
|
});
|
|
58
|
-
|
|
59
|
+
authConfigCache.logger = createLogger({
|
|
59
60
|
logger
|
|
60
61
|
});
|
|
61
|
-
|
|
62
|
+
authConfigCache.providers = createProviders({
|
|
62
63
|
authConfig,
|
|
63
64
|
logger,
|
|
64
65
|
plugins
|
|
65
66
|
});
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
authConfigCache.debug = authConfig.debug ?? logger?.isLevelEnabled('debug') === true;
|
|
68
|
+
authConfigCache.pages = authConfig.authPages;
|
|
69
|
+
authConfigCache.session = authConfig.session;
|
|
70
|
+
authConfigCache.theme = authConfig.theme;
|
|
71
|
+
authConfigCache.cookies = resolveCookies({
|
|
72
|
+
appMeta,
|
|
73
|
+
authConfig,
|
|
74
|
+
dev
|
|
75
|
+
});
|
|
76
|
+
// Auth.js v5 reads AUTH_SECRET; the v4 NEXTAUTH_SECRET variable is not
|
|
77
|
+
// supported — deployments must rename it (see the v5-to-v6 migration doc).
|
|
78
|
+
authConfigCache.secret = process.env.AUTH_SECRET;
|
|
79
|
+
// Self-hosted servers run behind arbitrary proxies; derive URLs from request
|
|
80
|
+
// headers (v4 derived them from NEXTAUTH_URL, aliased to AUTH_URL at startup).
|
|
81
|
+
authConfigCache.trustHost = true;
|
|
82
|
+
authConfigCache.basePath = '/api/auth';
|
|
72
83
|
initialized = true;
|
|
73
|
-
return
|
|
84
|
+
return authConfigCache;
|
|
74
85
|
}
|
|
75
|
-
export default
|
|
86
|
+
export default getAuthConfig;
|
|
@@ -0,0 +1,37 @@
|
|
|
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 createPrefixedCookies, { slugifyPrefix } from './createPrefixedCookies.js';
|
|
17
|
+
// Cookie name resolution, in order of precedence:
|
|
18
|
+
// 1. An explicit `auth.advanced.cookies` object is used verbatim.
|
|
19
|
+
// 2. An explicit `auth.advanced.cookiePrefix` namespaces cookie names (dev and prod).
|
|
20
|
+
// 3. When running the dev server, derive a prefix from the app slug/name so multiple
|
|
21
|
+
// apps on localhost do not share a cookie jar (browsers do not scope cookies by
|
|
22
|
+
// port). Production is left untouched, returning undefined for Auth.js defaults.
|
|
23
|
+
function resolveCookies({ appMeta, authConfig, dev }) {
|
|
24
|
+
const explicitCookies = authConfig?.advanced?.cookies;
|
|
25
|
+
if (!type.isNone(explicitCookies)) return explicitCookies;
|
|
26
|
+
const devPrefix = dev ? slugifyPrefix(appMeta?.slug ?? appMeta?.name ?? '') : '';
|
|
27
|
+
const prefix = authConfig?.advanced?.cookiePrefix ?? (devPrefix === '' ? undefined : devPrefix);
|
|
28
|
+
if (type.isNone(prefix) || prefix === '') return undefined;
|
|
29
|
+
// Auth.js v5 reads AUTH_URL with NEXTAUTH_URL as a v4 fallback.
|
|
30
|
+
const authUrl = process.env.AUTH_URL ?? process.env.NEXTAUTH_URL;
|
|
31
|
+
const useSecureCookies = authUrl?.startsWith('https://') ?? false;
|
|
32
|
+
return createPrefixedCookies({
|
|
33
|
+
prefix,
|
|
34
|
+
useSecureCookies
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export default resolveCookies;
|