opalserve 0.1.0 → 0.1.2

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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -65
  3. package/dist/cli/discover.js +9 -9
  4. package/dist/cli/discover.js.map +1 -1
  5. package/dist/cli/index.js +3 -2
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/registry/server.d.ts.map +1 -1
  8. package/dist/registry/server.js +9 -5
  9. package/dist/registry/server.js.map +1 -1
  10. package/package.json +9 -7
  11. package/AGENTS.md +0 -23
  12. package/src/cli/discover.ts +0 -223
  13. package/src/cli/index.ts +0 -40
  14. package/src/connectors/base.ts +0 -75
  15. package/src/connectors/custom.ts +0 -139
  16. package/src/connectors/github.ts +0 -195
  17. package/src/connectors/google-drive.ts +0 -217
  18. package/src/connectors/index.ts +0 -86
  19. package/src/connectors/postgres.ts +0 -148
  20. package/src/connectors/slack.ts +0 -188
  21. package/src/core/auth.ts +0 -109
  22. package/src/core/registry.ts +0 -301
  23. package/src/core/tokenizer.ts +0 -40
  24. package/src/governance/audit.ts +0 -182
  25. package/src/governance/index.ts +0 -4
  26. package/src/governance/policy.ts +0 -187
  27. package/src/governance/rate-limiter.ts +0 -95
  28. package/src/governance/types.ts +0 -100
  29. package/src/identity/access-control.ts +0 -119
  30. package/src/identity/index.ts +0 -3
  31. package/src/identity/manager.ts +0 -207
  32. package/src/identity/types.ts +0 -91
  33. package/src/index.ts +0 -16
  34. package/src/registry/server.ts +0 -195
  35. package/src/types/index.ts +0 -128
  36. package/src/utils/config.ts +0 -78
  37. package/src/utils/index.ts +0 -47
  38. package/src/workflow/engine.ts +0 -187
  39. package/src/workflow/index.ts +0 -3
  40. package/src/workflow/templates.ts +0 -220
  41. package/src/workflow/types.ts +0 -89
  42. package/tsconfig.json +0 -25
@@ -1,187 +0,0 @@
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
- }
@@ -1,95 +0,0 @@
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
- }
@@ -1,100 +0,0 @@
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
- }
@@ -1,119 +0,0 @@
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
- }
@@ -1,3 +0,0 @@
1
- export { IdentityManager } from './manager.js';
2
- export { AccessControl, type AccessDecision } from './access-control.js';
3
- export * from './types.js';