@mondaydotcomorg/atp-server 0.19.9 → 0.19.11
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 +1 -0
- package/dist/create-server.d.ts.map +1 -1
- package/dist/create-server.js +8 -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 +31 -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/graphql-loader.d.ts +3 -2
- package/dist/graphql-loader.d.ts.map +1 -1
- package/dist/graphql-loader.js +7 -3
- package/dist/graphql-loader.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 +28 -0
- 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 +283 -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 +283 -74
- 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 +10 -0
- package/src/executor/sandbox-builder.ts +32 -10
- package/src/explorer/index.ts +98 -8
- package/src/graphql-loader.ts +13 -5
- package/src/handlers/definitions.handler.ts +5 -2
- package/src/handlers/execute.handler.ts +30 -1
- package/src/http/request-handler.ts +70 -58
- package/src/index.ts +1 -0
- package/src/search/index.ts +18 -0
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import type { APIGroupConfig } from '@mondaydotcomorg/atp-protocol';
|
|
2
2
|
import { APIAggregator } from '../aggregator/index.js';
|
|
3
|
+
import { filterApiGroups } from '../core/request-scope.js';
|
|
3
4
|
|
|
4
5
|
export async function getDefinitions(apiGroups: APIGroupConfig[]): Promise<unknown> {
|
|
5
|
-
const
|
|
6
|
+
const filteredGroups = filterApiGroups(apiGroups);
|
|
7
|
+
|
|
8
|
+
const aggregator = new APIAggregator(filteredGroups);
|
|
6
9
|
const typescript = await aggregator.generateTypeScript();
|
|
7
10
|
|
|
8
11
|
return {
|
|
9
12
|
typescript,
|
|
10
|
-
apiGroups:
|
|
13
|
+
apiGroups: filteredGroups.map((g) => g.name),
|
|
11
14
|
version: '1.0.0',
|
|
12
15
|
};
|
|
13
16
|
}
|
|
@@ -2,7 +2,7 @@ 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
7
|
import { nanoid } from 'nanoid';
|
|
8
8
|
import {
|
|
@@ -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: nanoid(),
|
|
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,6 +120,8 @@ export async function handleExecute(
|
|
|
93
120
|
requestConfig.provenanceMode || config.execution.provenanceMode || ProvenanceMode.NONE,
|
|
94
121
|
securityPolicies: config.execution.securityPolicies || [],
|
|
95
122
|
provenanceHints: requestConfig.provenanceHints,
|
|
123
|
+
requestContext: requestConfig.requestContext,
|
|
124
|
+
onToolCall,
|
|
96
125
|
};
|
|
97
126
|
|
|
98
127
|
// Verify provenance hints if provided
|
|
@@ -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
|
}
|