@mondaydotcomorg/atp-server 0.19.10 → 0.19.12
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/core/config.d.ts +20 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/request-scope.d.ts +33 -0
- package/dist/core/request-scope.d.ts.map +1 -0
- package/dist/core/request-scope.js +93 -0
- package/dist/core/request-scope.js.map +1 -0
- package/dist/create-server.d.ts +2 -0
- package/dist/create-server.d.ts.map +1 -1
- package/dist/create-server.js +12 -0
- package/dist/create-server.js.map +1 -1
- package/dist/executor/sandbox-builder.d.ts.map +1 -1
- package/dist/executor/sandbox-builder.js +27 -9
- package/dist/executor/sandbox-builder.js.map +1 -1
- package/dist/explorer/index.d.ts +14 -1
- package/dist/explorer/index.d.ts.map +1 -1
- package/dist/explorer/index.js +75 -6
- package/dist/explorer/index.js.map +1 -1
- package/dist/handlers/definitions.handler.d.ts.map +1 -1
- package/dist/handlers/definitions.handler.js +4 -2
- package/dist/handlers/definitions.handler.js.map +1 -1
- package/dist/handlers/execute.handler.d.ts.map +1 -1
- package/dist/handlers/execute.handler.js +37 -5
- package/dist/handlers/execute.handler.js.map +1 -1
- package/dist/http/request-handler.d.ts +2 -1
- package/dist/http/request-handler.d.ts.map +1 -1
- package/dist/http/request-handler.js +68 -57
- package/dist/http/request-handler.js.map +1 -1
- package/dist/index.cjs +287 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +287 -75
- package/dist/index.js.map +1 -1
- package/dist/search/index.d.ts +2 -0
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +16 -0
- package/dist/search/index.js.map +1 -1
- package/package.json +6 -6
- package/src/core/config.ts +21 -0
- package/src/core/request-scope.ts +131 -0
- package/src/create-server.ts +14 -0
- package/src/executor/sandbox-builder.ts +28 -10
- package/src/explorer/index.ts +98 -8
- package/src/handlers/definitions.handler.ts +5 -2
- package/src/handlers/execute.handler.ts +39 -6
- package/src/http/request-handler.ts +70 -58
- package/src/index.ts +1 -0
- package/src/search/index.ts +18 -0
|
@@ -2,9 +2,9 @@ import type { RequestContext, ResolvedServerConfig } from '../core/config.js';
|
|
|
2
2
|
import type { SandboxExecutor } from '../executor/index.js';
|
|
3
3
|
import type { ExecutionStateManager } from '../execution-state/index.js';
|
|
4
4
|
import type { ClientSessionManager } from '../client-sessions.js';
|
|
5
|
-
import type { AuditSink, AuditEvent } from '@mondaydotcomorg/atp-protocol';
|
|
5
|
+
import type { AuditSink, AuditEvent, ToolCallEvent } from '@mondaydotcomorg/atp-protocol';
|
|
6
6
|
import { ExecutionStatus, ProvenanceMode } from '@mondaydotcomorg/atp-protocol';
|
|
7
|
-
import
|
|
7
|
+
import crypto from 'crypto';
|
|
8
8
|
import {
|
|
9
9
|
captureProvenanceSnapshot,
|
|
10
10
|
verifyProvenanceHints,
|
|
@@ -82,6 +82,33 @@ export async function handleExecute(
|
|
|
82
82
|
} catch (error) {}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
const onToolCall = auditSink
|
|
86
|
+
? (event: ToolCallEvent) => {
|
|
87
|
+
const auditEvent: AuditEvent = {
|
|
88
|
+
eventId: crypto.randomUUID(),
|
|
89
|
+
timestamp: Date.now(),
|
|
90
|
+
clientId: ctx.clientId || 'anonymous',
|
|
91
|
+
eventType: 'tool_call',
|
|
92
|
+
action: 'complete',
|
|
93
|
+
toolName: event.toolName,
|
|
94
|
+
apiGroup: event.apiGroup,
|
|
95
|
+
input: event.input,
|
|
96
|
+
output: event.output,
|
|
97
|
+
status: event.success ? 'success' : 'failed',
|
|
98
|
+
error: event.error
|
|
99
|
+
? {
|
|
100
|
+
message: event.error.message,
|
|
101
|
+
stack: event.error.stack,
|
|
102
|
+
}
|
|
103
|
+
: undefined,
|
|
104
|
+
metadata: {
|
|
105
|
+
duration: event.duration,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
auditSink.write(auditEvent).catch(() => {});
|
|
109
|
+
}
|
|
110
|
+
: undefined;
|
|
111
|
+
|
|
85
112
|
const executionConfig = {
|
|
86
113
|
timeout: requestConfig.timeout || config.execution.timeout,
|
|
87
114
|
maxMemory: memoryInBytes,
|
|
@@ -93,12 +120,18 @@ export async function handleExecute(
|
|
|
93
120
|
requestConfig.provenanceMode || config.execution.provenanceMode || ProvenanceMode.NONE,
|
|
94
121
|
securityPolicies: config.execution.securityPolicies || [],
|
|
95
122
|
provenanceHints: requestConfig.provenanceHints,
|
|
96
|
-
requestContext:
|
|
123
|
+
requestContext: {
|
|
124
|
+
...requestConfig.requestContext,
|
|
125
|
+
headers: ctx.headers,
|
|
126
|
+
path: ctx.path,
|
|
127
|
+
method: ctx.method,
|
|
128
|
+
},
|
|
129
|
+
onToolCall,
|
|
97
130
|
};
|
|
98
131
|
|
|
99
132
|
// Verify provenance hints if provided
|
|
100
133
|
let hintMap: Map<string, ProvenanceMetadata> | undefined;
|
|
101
|
-
const prelimExecutionId =
|
|
134
|
+
const prelimExecutionId = crypto.randomUUID();
|
|
102
135
|
if (
|
|
103
136
|
executionConfig.provenanceHints &&
|
|
104
137
|
executionConfig.provenanceHints.length > 0 &&
|
|
@@ -142,7 +175,7 @@ export async function handleExecute(
|
|
|
142
175
|
|
|
143
176
|
if (auditSink) {
|
|
144
177
|
const startEvent: AuditEvent = {
|
|
145
|
-
eventId:
|
|
178
|
+
eventId: crypto.randomUUID(),
|
|
146
179
|
timestamp: startTime,
|
|
147
180
|
clientId: ctx.clientId || 'anonymous',
|
|
148
181
|
eventType: 'execution',
|
|
@@ -215,7 +248,7 @@ export async function handleExecute(
|
|
|
215
248
|
|
|
216
249
|
if (auditSink) {
|
|
217
250
|
const endEvent: AuditEvent = {
|
|
218
|
-
eventId:
|
|
251
|
+
eventId: crypto.randomUUID(),
|
|
219
252
|
timestamp: Date.now(),
|
|
220
253
|
clientId: ctx.clientId || 'anonymous',
|
|
221
254
|
eventType: 'execution',
|
|
@@ -4,8 +4,9 @@ import { log } from '@mondaydotcomorg/atp-runtime';
|
|
|
4
4
|
import type { CacheProvider, AuthProvider, AuditSink } from '@mondaydotcomorg/atp-protocol';
|
|
5
5
|
import { parseBody } from '../core/http.js';
|
|
6
6
|
import { handleError, createContext } from '../utils/index.js';
|
|
7
|
-
import type { RequestContext, Middleware } from '../core/config.js';
|
|
7
|
+
import type { RequestContext, Middleware, ToolRulesProvider } from '../core/config.js';
|
|
8
8
|
import type { ClientSessionManager } from '../client-sessions.js';
|
|
9
|
+
import { runInRequestScope } from '../core/request-scope.js';
|
|
9
10
|
|
|
10
11
|
export interface RequestHandlerDeps {
|
|
11
12
|
cacheProvider?: CacheProvider;
|
|
@@ -15,6 +16,7 @@ export interface RequestHandlerDeps {
|
|
|
15
16
|
middleware: Middleware[];
|
|
16
17
|
routeHandler: (ctx: RequestContext) => Promise<void>;
|
|
17
18
|
sessionManager?: ClientSessionManager;
|
|
19
|
+
toolRulesProvider?: ToolRulesProvider;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export async function handleHTTPRequest(
|
|
@@ -34,75 +36,85 @@ export async function handleHTTPRequest(
|
|
|
34
36
|
const headers = new Map<string, string>();
|
|
35
37
|
responseHeaders.set(req, headers);
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
} else {
|
|
43
|
-
ctx.body = await parseBody(req);
|
|
44
|
-
}
|
|
39
|
+
if (deps.toolRulesProvider) {
|
|
40
|
+
try {
|
|
41
|
+
ctx.toolRules = deps.toolRulesProvider(ctx);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
log.warn('Tool rules provider failed', { error });
|
|
45
44
|
}
|
|
45
|
+
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
await runInRequestScope({ toolRules: ctx.toolRules }, async () => {
|
|
49
48
|
try {
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
} catch (error) {}
|
|
49
|
+
if (req.method === 'POST' || req.method === 'PUT') {
|
|
50
|
+
const reqWithBody = req as IncomingMessage & { body?: unknown };
|
|
51
|
+
if (reqWithBody.body !== undefined) {
|
|
52
|
+
ctx.body = reqWithBody.body;
|
|
53
|
+
} else {
|
|
54
|
+
ctx.body = await parseBody(req);
|
|
55
|
+
}
|
|
58
56
|
}
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
res.writeHead(ctx.status, {
|
|
62
|
-
'Content-Type': isStringResponse ? 'text/plain; charset=utf-8' : 'application/json',
|
|
63
|
-
...Object.fromEntries(headers),
|
|
64
|
-
});
|
|
65
|
-
res.end(isStringResponse ? ctx.responseBody : JSON.stringify(ctx.responseBody));
|
|
66
|
-
} catch (writeError) {}
|
|
67
|
-
} catch (error) {
|
|
68
|
-
try {
|
|
69
|
-
if (ctx.clientId && deps.sessionManager && ctx.path !== '/api/init') {
|
|
70
|
-
try {
|
|
71
|
-
const newToken = deps.sessionManager.generateToken(ctx.clientId);
|
|
72
|
-
const expiresAt = Date.now() + 60 * 60 * 1000;
|
|
58
|
+
await runMiddleware(ctx, deps.middleware, deps.routeHandler);
|
|
73
59
|
|
|
74
|
-
|
|
75
|
-
|
|
60
|
+
try {
|
|
61
|
+
if (ctx.clientId && deps.sessionManager && ctx.path !== '/api/init') {
|
|
62
|
+
try {
|
|
63
|
+
const newToken = deps.sessionManager.generateToken(ctx.clientId);
|
|
64
|
+
const expiresAt = Date.now() + 60 * 60 * 1000;
|
|
76
65
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
hasSessionManager: !!deps.sessionManager,
|
|
81
|
-
headerCount: headers.size,
|
|
82
|
-
});
|
|
83
|
-
} catch (tokenError) {
|
|
84
|
-
log.warn('Token refresh failed on error', { error: tokenError });
|
|
66
|
+
headers.set('X-ATP-Token', newToken);
|
|
67
|
+
headers.set('X-ATP-Token-Expires', expiresAt.toString());
|
|
68
|
+
} catch (error) {}
|
|
85
69
|
}
|
|
86
|
-
} else {
|
|
87
|
-
log.debug('Token refresh skipped on error', {
|
|
88
|
-
hasClientId: !!ctx.clientId,
|
|
89
|
-
hasSessionManager: !!deps.sessionManager,
|
|
90
|
-
path: ctx.path,
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
70
|
|
|
94
|
-
|
|
95
|
-
|
|
71
|
+
const isStringResponse = typeof ctx.responseBody === 'string';
|
|
72
|
+
res.writeHead(ctx.status, {
|
|
73
|
+
'Content-Type': isStringResponse ? 'text/plain; charset=utf-8' : 'application/json',
|
|
74
|
+
...Object.fromEntries(headers),
|
|
75
|
+
});
|
|
76
|
+
res.end(isStringResponse ? ctx.responseBody : JSON.stringify(ctx.responseBody));
|
|
77
|
+
} catch (writeError) {}
|
|
78
|
+
} catch (error) {
|
|
96
79
|
try {
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
80
|
+
if (ctx.clientId && deps.sessionManager && ctx.path !== '/api/init') {
|
|
81
|
+
try {
|
|
82
|
+
const newToken = deps.sessionManager.generateToken(ctx.clientId);
|
|
83
|
+
const expiresAt = Date.now() + 60 * 60 * 1000;
|
|
84
|
+
|
|
85
|
+
headers.set('X-ATP-Token', newToken);
|
|
86
|
+
headers.set('X-ATP-Token-Expires', expiresAt.toString());
|
|
87
|
+
|
|
88
|
+
log.debug('Token refresh headers set on error', {
|
|
89
|
+
clientId: ctx.clientId,
|
|
90
|
+
path: ctx.path,
|
|
91
|
+
hasSessionManager: !!deps.sessionManager,
|
|
92
|
+
headerCount: headers.size,
|
|
93
|
+
});
|
|
94
|
+
} catch (tokenError) {
|
|
95
|
+
log.warn('Token refresh failed on error', { error: tokenError });
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
log.debug('Token refresh skipped on error', {
|
|
99
|
+
hasClientId: !!ctx.clientId,
|
|
100
|
+
hasSessionManager: !!deps.sessionManager,
|
|
101
|
+
path: ctx.path,
|
|
102
|
+
});
|
|
100
103
|
}
|
|
101
|
-
|
|
104
|
+
|
|
105
|
+
handleError(res, error as Error, nanoid(), headers);
|
|
106
|
+
} catch (handlerError) {
|
|
107
|
+
try {
|
|
108
|
+
if (!res.headersSent) {
|
|
109
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
110
|
+
res.end(JSON.stringify({ error: 'Internal server error' }));
|
|
111
|
+
}
|
|
112
|
+
} catch {}
|
|
113
|
+
}
|
|
114
|
+
} finally {
|
|
115
|
+
responseHeaders.delete(req);
|
|
102
116
|
}
|
|
103
|
-
}
|
|
104
|
-
responseHeaders.delete(req);
|
|
105
|
-
}
|
|
117
|
+
});
|
|
106
118
|
}
|
|
107
119
|
|
|
108
120
|
async function runMiddleware(
|
package/src/index.ts
CHANGED
package/src/search/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
AuthProvider,
|
|
6
6
|
ScopeFilteringConfig,
|
|
7
7
|
} from '@mondaydotcomorg/atp-protocol';
|
|
8
|
+
import { filterApiGroups } from '../core/request-scope.js';
|
|
8
9
|
|
|
9
10
|
interface IndexedFunction {
|
|
10
11
|
apiGroup: string;
|
|
@@ -24,6 +25,7 @@ interface IndexedFunction {
|
|
|
24
25
|
*/
|
|
25
26
|
export class SearchEngine {
|
|
26
27
|
private index: IndexedFunction[] = [];
|
|
28
|
+
private apiGroups: APIGroupConfig[] = [];
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* Creates a new SearchEngine instance.
|
|
@@ -31,6 +33,7 @@ export class SearchEngine {
|
|
|
31
33
|
*/
|
|
32
34
|
constructor(apiGroups?: APIGroupConfig[]) {
|
|
33
35
|
if (apiGroups) {
|
|
36
|
+
this.apiGroups = apiGroups;
|
|
34
37
|
this.buildIndex(apiGroups);
|
|
35
38
|
}
|
|
36
39
|
}
|
|
@@ -67,6 +70,7 @@ export class SearchEngine {
|
|
|
67
70
|
|
|
68
71
|
/**
|
|
69
72
|
* Searches for API functions matching the query.
|
|
73
|
+
* Tool rules are automatically applied from the request scope.
|
|
70
74
|
* @param options - Search options including query and filters
|
|
71
75
|
* @param userId - Optional user ID for scope filtering
|
|
72
76
|
* @param authProvider - Optional auth provider for checking user scopes
|
|
@@ -79,10 +83,24 @@ export class SearchEngine {
|
|
|
79
83
|
authProvider?: AuthProvider,
|
|
80
84
|
scopeFilteringConfig?: ScopeFilteringConfig
|
|
81
85
|
): Promise<SearchResult[]> {
|
|
86
|
+
const allowedGroups = filterApiGroups(this.apiGroups);
|
|
87
|
+
const allowedTools = new Set<string>();
|
|
88
|
+
for (const group of allowedGroups) {
|
|
89
|
+
if (group.functions) {
|
|
90
|
+
for (const func of group.functions) {
|
|
91
|
+
allowedTools.add(`${group.name}:${func.name}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
82
96
|
const queryWords = this.extractKeywords(options.query);
|
|
83
97
|
const results: Array<{ result: SearchResult; score: number }> = [];
|
|
84
98
|
|
|
85
99
|
for (const item of this.index) {
|
|
100
|
+
if (!allowedTools.has(`${item.apiGroup}:${item.functionName}`)) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
86
104
|
if (options.apiGroups && !options.apiGroups.includes(item.apiGroup)) {
|
|
87
105
|
continue;
|
|
88
106
|
}
|