@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.
Files changed (54) hide show
  1. package/dist/core/config.d.ts +20 -1
  2. package/dist/core/config.d.ts.map +1 -1
  3. package/dist/core/config.js.map +1 -1
  4. package/dist/core/request-scope.d.ts +33 -0
  5. package/dist/core/request-scope.d.ts.map +1 -0
  6. package/dist/core/request-scope.js +93 -0
  7. package/dist/core/request-scope.js.map +1 -0
  8. package/dist/create-server.d.ts +1 -0
  9. package/dist/create-server.d.ts.map +1 -1
  10. package/dist/create-server.js +8 -0
  11. package/dist/create-server.js.map +1 -1
  12. package/dist/executor/sandbox-builder.d.ts.map +1 -1
  13. package/dist/executor/sandbox-builder.js +31 -9
  14. package/dist/executor/sandbox-builder.js.map +1 -1
  15. package/dist/explorer/index.d.ts +14 -1
  16. package/dist/explorer/index.d.ts.map +1 -1
  17. package/dist/explorer/index.js +75 -6
  18. package/dist/explorer/index.js.map +1 -1
  19. package/dist/graphql-loader.d.ts +3 -2
  20. package/dist/graphql-loader.d.ts.map +1 -1
  21. package/dist/graphql-loader.js +7 -3
  22. package/dist/graphql-loader.js.map +1 -1
  23. package/dist/handlers/definitions.handler.d.ts.map +1 -1
  24. package/dist/handlers/definitions.handler.js +4 -2
  25. package/dist/handlers/definitions.handler.js.map +1 -1
  26. package/dist/handlers/execute.handler.d.ts.map +1 -1
  27. package/dist/handlers/execute.handler.js +28 -0
  28. package/dist/handlers/execute.handler.js.map +1 -1
  29. package/dist/http/request-handler.d.ts +2 -1
  30. package/dist/http/request-handler.d.ts.map +1 -1
  31. package/dist/http/request-handler.js +68 -57
  32. package/dist/http/request-handler.js.map +1 -1
  33. package/dist/index.cjs +283 -74
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.ts +1 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +283 -74
  38. package/dist/index.js.map +1 -1
  39. package/dist/search/index.d.ts +2 -0
  40. package/dist/search/index.d.ts.map +1 -1
  41. package/dist/search/index.js +16 -0
  42. package/dist/search/index.js.map +1 -1
  43. package/package.json +6 -6
  44. package/src/core/config.ts +21 -0
  45. package/src/core/request-scope.ts +131 -0
  46. package/src/create-server.ts +10 -0
  47. package/src/executor/sandbox-builder.ts +32 -10
  48. package/src/explorer/index.ts +98 -8
  49. package/src/graphql-loader.ts +13 -5
  50. package/src/handlers/definitions.handler.ts +5 -2
  51. package/src/handlers/execute.handler.ts +30 -1
  52. package/src/http/request-handler.ts +70 -58
  53. package/src/index.ts +1 -0
  54. 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 aggregator = new APIAggregator(apiGroups);
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: apiGroups.map((g) => g.name),
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
- try {
38
- if (req.method === 'POST' || req.method === 'PUT') {
39
- const reqWithBody = req as IncomingMessage & { body?: unknown };
40
- if (reqWithBody.body !== undefined) {
41
- ctx.body = reqWithBody.body;
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
- await runMiddleware(ctx, deps.middleware, deps.routeHandler);
48
-
47
+ await runInRequestScope({ toolRules: ctx.toolRules }, async () => {
49
48
  try {
50
- if (ctx.clientId && deps.sessionManager && ctx.path !== '/api/init') {
51
- try {
52
- const newToken = deps.sessionManager.generateToken(ctx.clientId);
53
- const expiresAt = Date.now() + 60 * 60 * 1000;
54
-
55
- headers.set('X-ATP-Token', newToken);
56
- headers.set('X-ATP-Token-Expires', expiresAt.toString());
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
- const isStringResponse = typeof ctx.responseBody === 'string';
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
- headers.set('X-ATP-Token', newToken);
75
- headers.set('X-ATP-Token-Expires', expiresAt.toString());
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
- log.debug('Token refresh headers set on error', {
78
- clientId: ctx.clientId,
79
- path: ctx.path,
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
- handleError(res, error as Error, nanoid(), headers);
95
- } catch (handlerError) {
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 (!res.headersSent) {
98
- res.writeHead(500, { 'Content-Type': 'application/json' });
99
- res.end(JSON.stringify({ error: 'Internal server error' }));
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
- } catch {}
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
- } finally {
104
- responseHeaders.delete(req);
105
- }
117
+ });
106
118
  }
107
119
 
108
120
  async function runMiddleware(
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ export type {
13
13
  RequestContext,
14
14
  } from './core/config.js';
15
15
  export { MB, GB, SECOND, MINUTE, HOUR, DAY } from './core/config.js';
16
+ export type { ToolRulesProvider } from './core/config.js';
16
17
 
17
18
  export type {
18
19
  ProvenanceMetadata,
@@ -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
  }