opalserve 0.1.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.
Files changed (158) hide show
  1. package/.env.example +19 -0
  2. package/AGENTS.md +23 -0
  3. package/README.md +109 -0
  4. package/config/servers.example.yaml +67 -0
  5. package/config/servers.yaml +2 -0
  6. package/dist/cli/discover.d.ts +3 -0
  7. package/dist/cli/discover.d.ts.map +1 -0
  8. package/dist/cli/discover.js +160 -0
  9. package/dist/cli/discover.js.map +1 -0
  10. package/dist/cli/index.d.ts +3 -0
  11. package/dist/cli/index.d.ts.map +1 -0
  12. package/dist/cli/index.js +32 -0
  13. package/dist/cli/index.js.map +1 -0
  14. package/dist/connectors/base.d.ts +49 -0
  15. package/dist/connectors/base.d.ts.map +1 -0
  16. package/dist/connectors/base.js +45 -0
  17. package/dist/connectors/base.js.map +1 -0
  18. package/dist/connectors/custom.d.ts +19 -0
  19. package/dist/connectors/custom.d.ts.map +1 -0
  20. package/dist/connectors/custom.js +129 -0
  21. package/dist/connectors/custom.js.map +1 -0
  22. package/dist/connectors/github.d.ts +18 -0
  23. package/dist/connectors/github.d.ts.map +1 -0
  24. package/dist/connectors/github.js +188 -0
  25. package/dist/connectors/github.js.map +1 -0
  26. package/dist/connectors/google-drive.d.ts +18 -0
  27. package/dist/connectors/google-drive.d.ts.map +1 -0
  28. package/dist/connectors/google-drive.js +209 -0
  29. package/dist/connectors/google-drive.js.map +1 -0
  30. package/dist/connectors/index.d.ts +11 -0
  31. package/dist/connectors/index.d.ts.map +1 -0
  32. package/dist/connectors/index.js +76 -0
  33. package/dist/connectors/index.js.map +1 -0
  34. package/dist/connectors/postgres.d.ts +18 -0
  35. package/dist/connectors/postgres.d.ts.map +1 -0
  36. package/dist/connectors/postgres.js +140 -0
  37. package/dist/connectors/postgres.js.map +1 -0
  38. package/dist/connectors/slack.d.ts +18 -0
  39. package/dist/connectors/slack.d.ts.map +1 -0
  40. package/dist/connectors/slack.js +181 -0
  41. package/dist/connectors/slack.js.map +1 -0
  42. package/dist/core/auth.d.ts +26 -0
  43. package/dist/core/auth.d.ts.map +1 -0
  44. package/dist/core/auth.js +81 -0
  45. package/dist/core/auth.js.map +1 -0
  46. package/dist/core/registry.d.ts +33 -0
  47. package/dist/core/registry.d.ts.map +1 -0
  48. package/dist/core/registry.js +237 -0
  49. package/dist/core/registry.js.map +1 -0
  50. package/dist/core/tokenizer.d.ts +16 -0
  51. package/dist/core/tokenizer.d.ts.map +1 -0
  52. package/dist/core/tokenizer.js +29 -0
  53. package/dist/core/tokenizer.js.map +1 -0
  54. package/dist/governance/audit.d.ts +27 -0
  55. package/dist/governance/audit.d.ts.map +1 -0
  56. package/dist/governance/audit.js +149 -0
  57. package/dist/governance/audit.js.map +1 -0
  58. package/dist/governance/index.d.ts +5 -0
  59. package/dist/governance/index.d.ts.map +1 -0
  60. package/dist/governance/index.js +5 -0
  61. package/dist/governance/index.js.map +1 -0
  62. package/dist/governance/policy.d.ts +20 -0
  63. package/dist/governance/policy.d.ts.map +1 -0
  64. package/dist/governance/policy.js +162 -0
  65. package/dist/governance/policy.js.map +1 -0
  66. package/dist/governance/rate-limiter.d.ts +20 -0
  67. package/dist/governance/rate-limiter.d.ts.map +1 -0
  68. package/dist/governance/rate-limiter.js +73 -0
  69. package/dist/governance/rate-limiter.js.map +1 -0
  70. package/dist/governance/types.d.ts +246 -0
  71. package/dist/governance/types.d.ts.map +1 -0
  72. package/dist/governance/types.js +72 -0
  73. package/dist/governance/types.js.map +1 -0
  74. package/dist/identity/access-control.d.ts +15 -0
  75. package/dist/identity/access-control.d.ts.map +1 -0
  76. package/dist/identity/access-control.js +81 -0
  77. package/dist/identity/access-control.js.map +1 -0
  78. package/dist/identity/index.d.ts +4 -0
  79. package/dist/identity/index.d.ts.map +1 -0
  80. package/dist/identity/index.js +4 -0
  81. package/dist/identity/index.js.map +1 -0
  82. package/dist/identity/manager.d.ts +29 -0
  83. package/dist/identity/manager.d.ts.map +1 -0
  84. package/dist/identity/manager.js +167 -0
  85. package/dist/identity/manager.js.map +1 -0
  86. package/dist/identity/types.d.ts +237 -0
  87. package/dist/identity/types.d.ts.map +1 -0
  88. package/dist/identity/types.js +80 -0
  89. package/dist/identity/types.js.map +1 -0
  90. package/dist/index.d.ts +13 -0
  91. package/dist/index.d.ts.map +1 -0
  92. package/dist/index.js +10 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/registry/server.d.ts +14 -0
  95. package/dist/registry/server.d.ts.map +1 -0
  96. package/dist/registry/server.js +173 -0
  97. package/dist/registry/server.js.map +1 -0
  98. package/dist/types/index.d.ts +639 -0
  99. package/dist/types/index.d.ts.map +1 -0
  100. package/dist/types/index.js +76 -0
  101. package/dist/types/index.js.map +1 -0
  102. package/dist/utils/config.d.ts +29 -0
  103. package/dist/utils/config.d.ts.map +1 -0
  104. package/dist/utils/config.js +47 -0
  105. package/dist/utils/config.js.map +1 -0
  106. package/dist/utils/index.d.ts +7 -0
  107. package/dist/utils/index.d.ts.map +1 -0
  108. package/dist/utils/index.js +44 -0
  109. package/dist/utils/index.js.map +1 -0
  110. package/dist/workflow/engine.d.ts +18 -0
  111. package/dist/workflow/engine.d.ts.map +1 -0
  112. package/dist/workflow/engine.js +155 -0
  113. package/dist/workflow/engine.js.map +1 -0
  114. package/dist/workflow/index.d.ts +4 -0
  115. package/dist/workflow/index.d.ts.map +1 -0
  116. package/dist/workflow/index.js +4 -0
  117. package/dist/workflow/index.js.map +1 -0
  118. package/dist/workflow/templates.d.ts +4 -0
  119. package/dist/workflow/templates.d.ts.map +1 -0
  120. package/dist/workflow/templates.js +218 -0
  121. package/dist/workflow/templates.js.map +1 -0
  122. package/dist/workflow/types.d.ts +255 -0
  123. package/dist/workflow/types.d.ts.map +1 -0
  124. package/dist/workflow/types.js +48 -0
  125. package/dist/workflow/types.js.map +1 -0
  126. package/eslint.config.js +25 -0
  127. package/package.json +78 -0
  128. package/src/cli/discover.ts +223 -0
  129. package/src/cli/index.ts +40 -0
  130. package/src/connectors/base.ts +75 -0
  131. package/src/connectors/custom.ts +139 -0
  132. package/src/connectors/github.ts +195 -0
  133. package/src/connectors/google-drive.ts +217 -0
  134. package/src/connectors/index.ts +86 -0
  135. package/src/connectors/postgres.ts +148 -0
  136. package/src/connectors/slack.ts +188 -0
  137. package/src/core/auth.ts +109 -0
  138. package/src/core/registry.ts +301 -0
  139. package/src/core/tokenizer.ts +40 -0
  140. package/src/governance/audit.ts +182 -0
  141. package/src/governance/index.ts +4 -0
  142. package/src/governance/policy.ts +187 -0
  143. package/src/governance/rate-limiter.ts +95 -0
  144. package/src/governance/types.ts +100 -0
  145. package/src/identity/access-control.ts +119 -0
  146. package/src/identity/index.ts +3 -0
  147. package/src/identity/manager.ts +207 -0
  148. package/src/identity/types.ts +91 -0
  149. package/src/index.ts +16 -0
  150. package/src/registry/server.ts +195 -0
  151. package/src/types/index.ts +128 -0
  152. package/src/utils/config.ts +78 -0
  153. package/src/utils/index.ts +47 -0
  154. package/src/workflow/engine.ts +187 -0
  155. package/src/workflow/index.ts +3 -0
  156. package/src/workflow/templates.ts +220 -0
  157. package/src/workflow/types.ts +89 -0
  158. package/tsconfig.json +25 -0
@@ -0,0 +1,187 @@
1
+ import type { GovernancePolicy, PolicyRule, AuditEvent } from './types.js';
2
+
3
+ export class PolicyEngine {
4
+ private policies: Map<string, GovernancePolicy> = new Map();
5
+ private auditCallback?: (event: Omit<AuditEvent, 'id' | 'timestamp'>) => void;
6
+
7
+ setAuditCallback(callback: (event: Omit<AuditEvent, 'id' | 'timestamp'>) => void): void {
8
+ this.auditCallback = callback;
9
+ }
10
+
11
+ registerPolicy(policy: GovernancePolicy): void {
12
+ policy.rules.sort((a, b) => b.priority - a.priority);
13
+ this.policies.set(policy.id, policy);
14
+ }
15
+
16
+ getPolicy(id: string): GovernancePolicy | undefined {
17
+ return this.policies.get(id);
18
+ }
19
+
20
+ getAllPolicies(): GovernancePolicy[] {
21
+ return Array.from(this.policies.values());
22
+ }
23
+
24
+ deletePolicy(id: string): boolean {
25
+ return this.policies.delete(id);
26
+ }
27
+
28
+ evaluate(context: Record<string, unknown>): {
29
+ allowed: boolean;
30
+ matchedRules: PolicyRule[];
31
+ auditEvents: Omit<AuditEvent, 'id' | 'timestamp'>[];
32
+ } {
33
+ const auditEvents: Omit<AuditEvent, 'id' | 'timestamp'>[] = [];
34
+ const matchedRules: PolicyRule[] = [];
35
+
36
+ for (const policy of this.policies.values()) {
37
+ if (!policy.enabled) continue;
38
+
39
+ for (const rule of policy.rules) {
40
+ if (!rule.enabled) continue;
41
+
42
+ const matches = this.evaluateConditions(rule.conditions, context);
43
+
44
+ if (matches) {
45
+ matchedRules.push(rule);
46
+
47
+ if (policy.auditEnabled && this.auditCallback) {
48
+ auditEvents.push({
49
+ type: 'admin.action',
50
+ requestId: context.requestId as string || `policy-${Date.now()}`,
51
+ agentId: context.agentId as string,
52
+ agentName: context.agentName as string,
53
+ action: `policy:${policy.name}:${rule.name}`,
54
+ result: rule.effect === 'allow' ? 'success' : rule.effect === 'deny' ? 'denied' : 'success',
55
+ metadata: { policyId: policy.id, ruleId: rule.id },
56
+ context: {},
57
+ });
58
+ }
59
+
60
+ if (rule.effect === 'deny') {
61
+ return { allowed: false, matchedRules, auditEvents };
62
+ }
63
+
64
+ if (rule.effect === 'allow') {
65
+ return { allowed: true, matchedRules, auditEvents };
66
+ }
67
+ }
68
+ }
69
+
70
+ if (matchedRules.length === 0) {
71
+ return {
72
+ allowed: policy.defaultEffect === 'allow',
73
+ matchedRules: [],
74
+ auditEvents,
75
+ };
76
+ }
77
+ }
78
+
79
+ return { allowed: true, matchedRules, auditEvents };
80
+ }
81
+
82
+ private evaluateConditions(conditions: PolicyRule['conditions'], context: Record<string, unknown>): boolean {
83
+ if (conditions.length === 0) return true;
84
+
85
+ return conditions.every(condition => {
86
+ const value = this.getNestedValue(context, condition.field);
87
+ return this.evaluateOperator(condition.operator, value, condition.value);
88
+ });
89
+ }
90
+
91
+ private getNestedValue(obj: Record<string, unknown>, path: string): unknown {
92
+ const keys = path.split('.');
93
+ let current: unknown = obj;
94
+
95
+ for (const key of keys) {
96
+ if (current === null || current === undefined) return undefined;
97
+ current = (current as Record<string, unknown>)[key];
98
+ }
99
+
100
+ return current;
101
+ }
102
+
103
+ private evaluateOperator(operator: PolicyRule['conditions'][0]['operator'], actual: unknown, expected: unknown): boolean {
104
+ switch (operator) {
105
+ case 'equals':
106
+ return actual === expected;
107
+ case 'not_equals':
108
+ return actual !== expected;
109
+ case 'contains':
110
+ return typeof actual === 'string' && typeof expected === 'string' && actual.includes(expected);
111
+ case 'not_contains':
112
+ return typeof actual === 'string' && typeof expected === 'string' && !actual.includes(expected);
113
+ case 'in':
114
+ return Array.isArray(expected) && expected.includes(actual);
115
+ case 'not_in':
116
+ return Array.isArray(expected) && !expected.includes(actual);
117
+ case 'greater_than':
118
+ return typeof actual === 'number' && typeof expected === 'number' && actual > expected;
119
+ case 'less_than':
120
+ return typeof actual === 'number' && typeof expected === 'number' && actual < expected;
121
+ default:
122
+ return false;
123
+ }
124
+ }
125
+
126
+ createDefaultPolicies(): void {
127
+ const highValueToolsPolicy: GovernancePolicy = {
128
+ id: 'high-value-tools',
129
+ name: 'High-Value Tool Protection',
130
+ description: 'Protect sensitive tools requiring additional verification',
131
+ version: '1.0.0',
132
+ enabled: true,
133
+ rules: [
134
+ {
135
+ id: 'deny-database-write',
136
+ name: 'Deny Database Writes',
137
+ description: 'Block write operations to production databases',
138
+ enabled: true,
139
+ priority: 100,
140
+ conditions: [
141
+ { field: 'tool.capabilities', operator: 'contains', value: 'write' },
142
+ { field: 'tool.serverName', operator: 'contains', value: 'postgres' },
143
+ ],
144
+ effect: 'deny',
145
+ actions: ['notify-admin'],
146
+ metadata: {},
147
+ },
148
+ ],
149
+ defaultEffect: 'allow',
150
+ auditEnabled: true,
151
+ complianceFrameworks: ['SOC2', 'GDPR'],
152
+ createdAt: new Date().toISOString(),
153
+ updatedAt: new Date().toISOString(),
154
+ };
155
+
156
+ const trustLevelPolicy: GovernancePolicy = {
157
+ id: 'trust-level-access',
158
+ name: 'Trust Level Access Control',
159
+ description: 'Restrict access based on identity trust level',
160
+ version: '1.0.0',
161
+ enabled: true,
162
+ rules: [
163
+ {
164
+ id: 'low-trust-limited-tools',
165
+ name: 'Low Trust Limited Access',
166
+ description: 'Limit tools for low-trust identities',
167
+ enabled: true,
168
+ priority: 50,
169
+ conditions: [
170
+ { field: 'identity.trustLevel', operator: 'in', value: ['untrusted', 'low'] },
171
+ ],
172
+ effect: 'deny',
173
+ actions: ['require-reapproval'],
174
+ metadata: {},
175
+ },
176
+ ],
177
+ defaultEffect: 'allow',
178
+ auditEnabled: true,
179
+ complianceFrameworks: ['ISO27001'],
180
+ createdAt: new Date().toISOString(),
181
+ updatedAt: new Date().toISOString(),
182
+ };
183
+
184
+ this.registerPolicy(highValueToolsPolicy);
185
+ this.registerPolicy(trustLevelPolicy);
186
+ }
187
+ }
@@ -0,0 +1,95 @@
1
+ import type { RateLimitState } from './types.js';
2
+
3
+ export interface RateLimitConfig {
4
+ windowMs: number;
5
+ maxRequests: number;
6
+ blockDurationMs: number;
7
+ }
8
+
9
+ export class RateLimiter {
10
+ private states: Map<string, RateLimitState> = new Map();
11
+ private configs: Map<string, RateLimitConfig> = new Map();
12
+
13
+ configure(identityId: string, config: RateLimitConfig): void {
14
+ this.configs.set(identityId, config);
15
+ }
16
+
17
+ check(identityId: string): { allowed: boolean; remaining: number; resetAt: number } {
18
+ const config = this.configs.get(identityId);
19
+ if (!config) {
20
+ return { allowed: true, remaining: -1, resetAt: 0 };
21
+ }
22
+
23
+ const now = Date.now();
24
+ let state = this.states.get(identityId);
25
+
26
+ if (!state || now - state.windowStart >= config.windowMs) {
27
+ state = {
28
+ identityId,
29
+ windowStart: now,
30
+ count: 0,
31
+ blocked: false,
32
+ blockedUntil: null,
33
+ };
34
+ this.states.set(identityId, state);
35
+ }
36
+
37
+ if (state.blocked && state.blockedUntil && now < state.blockedUntil) {
38
+ return {
39
+ allowed: false,
40
+ remaining: 0,
41
+ resetAt: state.blockedUntil,
42
+ };
43
+ }
44
+
45
+ if (state.blocked && state.blockedUntil && now >= state.blockedUntil) {
46
+ state.blocked = false;
47
+ state.blockedUntil = null;
48
+ state.windowStart = now;
49
+ state.count = 0;
50
+ }
51
+
52
+ state.count++;
53
+ this.states.set(identityId, state);
54
+
55
+ if (state.count > config.maxRequests) {
56
+ state.blocked = true;
57
+ state.blockedUntil = now + config.blockDurationMs;
58
+ this.states.set(identityId, state);
59
+
60
+ return {
61
+ allowed: false,
62
+ remaining: 0,
63
+ resetAt: state.blockedUntil,
64
+ };
65
+ }
66
+
67
+ return {
68
+ allowed: true,
69
+ remaining: config.maxRequests - state.count,
70
+ resetAt: state.windowStart + config.windowMs,
71
+ };
72
+ }
73
+
74
+ reset(identityId: string): void {
75
+ this.states.delete(identityId);
76
+ }
77
+
78
+ getState(identityId: string): RateLimitState | undefined {
79
+ return this.states.get(identityId);
80
+ }
81
+
82
+ cleanup(maxAgeMs: number): number {
83
+ const now = Date.now();
84
+ let cleaned = 0;
85
+
86
+ for (const [identityId, state] of this.states.entries()) {
87
+ if (now - state.windowStart > maxAgeMs && !state.blocked) {
88
+ this.states.delete(identityId);
89
+ cleaned++;
90
+ }
91
+ }
92
+
93
+ return cleaned;
94
+ }
95
+ }
@@ -0,0 +1,100 @@
1
+ import { z } from 'zod';
2
+
3
+ export const AuditEventTypeSchema = z.enum([
4
+ 'identity.created',
5
+ 'identity.updated',
6
+ 'identity.deleted',
7
+ 'identity.authenticated',
8
+ 'identity.auth_failed',
9
+ 'tool.accessed',
10
+ 'tool.executed',
11
+ 'tool.denied',
12
+ 'tool.error',
13
+ 'server.registered',
14
+ 'server.deregistered',
15
+ 'server.health_check',
16
+ 'workflow.created',
17
+ 'workflow.executed',
18
+ 'workflow.failed',
19
+ 'workflow.completed',
20
+ 'permission.checked',
21
+ 'permission.denied',
22
+ 'rate_limit.exceeded',
23
+ 'token.issued',
24
+ 'token.revoked',
25
+ 'admin.action',
26
+ ]);
27
+
28
+ export const AuditEventSchema = z.object({
29
+ id: z.string(),
30
+ type: AuditEventTypeSchema,
31
+ timestamp: z.string(),
32
+ agentId: z.string().optional(),
33
+ agentName: z.string().optional(),
34
+ sessionId: z.string().optional(),
35
+ requestId: z.string(),
36
+ ipAddress: z.string().optional(),
37
+ userAgent: z.string().optional(),
38
+ resourceType: z.string().optional(),
39
+ resourceId: z.string().optional(),
40
+ action: z.string(),
41
+ result: z.enum(['success', 'denied', 'error']),
42
+ reason: z.string().optional(),
43
+ metadata: z.record(z.any()).default({}),
44
+ context: z.record(z.any()).default({}),
45
+ });
46
+
47
+ export const PolicyRuleSchema = z.object({
48
+ id: z.string(),
49
+ name: z.string(),
50
+ description: z.string().optional(),
51
+ enabled: z.boolean().default(true),
52
+ priority: z.number().default(0),
53
+ conditions: z.array(z.object({
54
+ field: z.string(),
55
+ operator: z.enum(['equals', 'not_equals', 'contains', 'not_contains', 'in', 'not_in', 'greater_than', 'less_than']),
56
+ value: z.unknown(),
57
+ })).default([]),
58
+ effect: z.enum(['allow', 'deny', 'audit', 'rate_limit']),
59
+ actions: z.array(z.string()).default([]),
60
+ metadata: z.record(z.any()).default({}),
61
+ });
62
+
63
+ export const GovernancePolicySchema = z.object({
64
+ id: z.string(),
65
+ name: z.string(),
66
+ description: z.string(),
67
+ version: z.string().default('1.0.0'),
68
+ enabled: z.boolean().default(true),
69
+ rules: z.array(PolicyRuleSchema).default([]),
70
+ defaultEffect: z.enum(['allow', 'deny']).default('allow'),
71
+ auditEnabled: z.boolean().default(true),
72
+ complianceFrameworks: z.array(z.string()).default([]),
73
+ createdAt: z.string(),
74
+ updatedAt: z.string(),
75
+ });
76
+
77
+ export type AuditEventType = z.infer<typeof AuditEventTypeSchema>;
78
+ export type AuditEvent = z.infer<typeof AuditEventSchema>;
79
+ export type PolicyRule = z.infer<typeof PolicyRuleSchema>;
80
+ export type GovernancePolicy = z.infer<typeof GovernancePolicySchema>;
81
+
82
+ export interface RateLimitState {
83
+ identityId: string;
84
+ windowStart: number;
85
+ count: number;
86
+ blocked: boolean;
87
+ blockedUntil: number | null;
88
+ }
89
+
90
+ export interface ComplianceReport {
91
+ generatedAt: string;
92
+ period: { start: string; end: string };
93
+ totalEvents: number;
94
+ byType: Record<string, number>;
95
+ byAgent: Record<string, number>;
96
+ deniedAccess: number;
97
+ rateLimitExceeded: number;
98
+ policyViolations: number;
99
+ recommendations: string[];
100
+ }
@@ -0,0 +1,119 @@
1
+ import type { IdentityContext, Permission } from './types.js';
2
+ import type { Tool, Server } from '../types/index.js';
3
+
4
+ export interface AccessDecision {
5
+ allowed: boolean;
6
+ reason: string;
7
+ constraints?: Record<string, unknown>;
8
+ }
9
+
10
+ export class AccessControl {
11
+ checkToolAccess(context: IdentityContext, tool: Tool): AccessDecision {
12
+ if (context.permissions.includes('admin')) {
13
+ return { allowed: true, reason: 'Admin has full access' };
14
+ }
15
+
16
+ const toolId = tool.id;
17
+
18
+ if (context.constraints.deniedTools?.some((pattern: unknown) =>
19
+ toolId === pattern || (typeof pattern === 'string' && toolId.includes(pattern.replace('*', '')))
20
+ )) {
21
+ return { allowed: false, reason: 'Tool is explicitly denied' };
22
+ }
23
+
24
+ if (context.constraints.allowedTools?.length &&
25
+ !context.constraints.allowedTools.some((pattern: unknown) =>
26
+ toolId === pattern || (typeof pattern === 'string' && toolId.includes(pattern.replace('*', '')))
27
+ )) {
28
+ return { allowed: false, reason: 'Tool not in allowed list' };
29
+ }
30
+
31
+ const serverAccess = this.checkServerAccess(context, { id: tool.serverId } as Server);
32
+ if (!serverAccess.allowed) {
33
+ return { allowed: false, reason: `Server access denied: ${serverAccess.reason}` };
34
+ }
35
+
36
+ const hasCapability = tool.capabilities.some(cap =>
37
+ context.permissions.includes(`tools:${cap}` as Permission) ||
38
+ context.permissions.includes('tools:execute')
39
+ );
40
+
41
+ if (!hasCapability) {
42
+ return { allowed: false, reason: 'Missing required capability' };
43
+ }
44
+
45
+ return {
46
+ allowed: true,
47
+ reason: 'Access granted',
48
+ constraints: {
49
+ maxTools: context.constraints.maxToolsPerRequest,
50
+ timeout: tool.contextRequirements?.maxTokens ? tool.contextRequirements.maxTokens * 10 : 60000,
51
+ },
52
+ };
53
+ }
54
+
55
+ checkServerAccess(context: IdentityContext, server: Server): AccessDecision {
56
+ if (context.permissions.includes('admin')) {
57
+ return { allowed: true, reason: 'Admin has full access' };
58
+ }
59
+
60
+ const serverId = server.id;
61
+
62
+ if (context.constraints.deniedServers?.includes(serverId)) {
63
+ return { allowed: false, reason: 'Server is explicitly denied' };
64
+ }
65
+
66
+ if (context.constraints.allowedServers?.length &&
67
+ !context.constraints.allowedServers.includes(serverId)) {
68
+ return { allowed: false, reason: 'Server not in allowed list' };
69
+ }
70
+
71
+ return { allowed: true, reason: 'Server access granted' };
72
+ }
73
+
74
+ checkPermission(context: IdentityContext, permission: Permission): AccessDecision {
75
+ if (context.permissions.includes('admin') || context.permissions.includes(permission)) {
76
+ return { allowed: true, reason: 'Permission granted' };
77
+ }
78
+ return { allowed: false, reason: `Missing permission: ${permission}` };
79
+ }
80
+
81
+ checkRateLimit(context: IdentityContext, currentCount: number, window: 'minute' | 'hour'): AccessDecision {
82
+ const limit = window === 'minute'
83
+ ? context.constraints.rateLimitPerMinute
84
+ : context.constraints.rateLimitPerHour;
85
+
86
+ if (!limit) {
87
+ return { allowed: true, reason: 'No rate limit configured' };
88
+ }
89
+
90
+ if (currentCount >= limit) {
91
+ return {
92
+ allowed: false,
93
+ reason: `Rate limit exceeded: ${currentCount}/${limit} per ${window}`,
94
+ };
95
+ }
96
+
97
+ return { allowed: true, reason: 'Within rate limit' };
98
+ }
99
+
100
+ sanitizeContext(context: IdentityContext, tool: Tool): Record<string, unknown> {
101
+ const sanitized: Record<string, unknown> = {
102
+ agentId: context.agentId,
103
+ agentName: context.agentName,
104
+ agentType: context.agentType,
105
+ permissions: context.permissions.filter((p: Permission) =>
106
+ tool.capabilities.some(c => p.includes(c) || p === 'admin' || p === 'tools:execute')
107
+ ),
108
+ constraints: {
109
+ maxToolsPerRequest: Math.min(
110
+ context.constraints.maxToolsPerRequest as number || 10,
111
+ tool.contextRequirements?.maxTokens ? 5 : 10
112
+ ),
113
+ },
114
+ metadata: {},
115
+ };
116
+
117
+ return sanitized;
118
+ }
119
+ }
@@ -0,0 +1,3 @@
1
+ export { IdentityManager } from './manager.js';
2
+ export { AccessControl, type AccessDecision } from './access-control.js';
3
+ export * from './types.js';