@auxiora/connectors 1.0.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 (44) hide show
  1. package/LICENSE +191 -0
  2. package/dist/auth-manager.d.ts +27 -0
  3. package/dist/auth-manager.d.ts.map +1 -0
  4. package/dist/auth-manager.js +138 -0
  5. package/dist/auth-manager.js.map +1 -0
  6. package/dist/define-connector.d.ts +19 -0
  7. package/dist/define-connector.d.ts.map +1 -0
  8. package/dist/define-connector.js +27 -0
  9. package/dist/define-connector.js.map +1 -0
  10. package/dist/executor.d.ts +19 -0
  11. package/dist/executor.d.ts.map +1 -0
  12. package/dist/executor.js +72 -0
  13. package/dist/executor.js.map +1 -0
  14. package/dist/index.d.ts +7 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +6 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/registry.d.ts +13 -0
  19. package/dist/registry.d.ts.map +1 -0
  20. package/dist/registry.js +33 -0
  21. package/dist/registry.js.map +1 -0
  22. package/dist/trigger-manager.d.ts +19 -0
  23. package/dist/trigger-manager.d.ts.map +1 -0
  24. package/dist/trigger-manager.js +62 -0
  25. package/dist/trigger-manager.js.map +1 -0
  26. package/dist/types.d.ts +101 -0
  27. package/dist/types.d.ts.map +1 -0
  28. package/dist/types.js +2 -0
  29. package/dist/types.js.map +1 -0
  30. package/package.json +26 -0
  31. package/src/auth-manager.ts +177 -0
  32. package/src/define-connector.ts +45 -0
  33. package/src/executor.ts +108 -0
  34. package/src/index.ts +18 -0
  35. package/src/registry.ts +42 -0
  36. package/src/trigger-manager.ts +95 -0
  37. package/src/types.ts +111 -0
  38. package/tests/auth-manager.test.ts +136 -0
  39. package/tests/define-connector.test.ts +61 -0
  40. package/tests/executor.test.ts +122 -0
  41. package/tests/registry.test.ts +101 -0
  42. package/tests/trigger-manager.test.ts +135 -0
  43. package/tsconfig.json +12 -0
  44. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,62 @@
1
+ export class TriggerManager {
2
+ registry;
3
+ authManager;
4
+ subscriptions = new Map();
5
+ constructor(registry, authManager) {
6
+ this.registry = registry;
7
+ this.authManager = authManager;
8
+ }
9
+ /** Subscribe to a trigger on a connector instance. Returns a subscription ID. */
10
+ subscribe(connectorId, triggerId, instanceId, handler) {
11
+ const connector = this.registry.get(connectorId);
12
+ if (!connector) {
13
+ throw new Error(`Connector "${connectorId}" not found`);
14
+ }
15
+ const trigger = connector.triggers.find((t) => t.id === triggerId);
16
+ if (!trigger) {
17
+ throw new Error(`Trigger "${triggerId}" not found in connector "${connectorId}"`);
18
+ }
19
+ const subId = `${connectorId}:${triggerId}:${instanceId}`;
20
+ this.subscriptions.set(subId, {
21
+ connectorId,
22
+ triggerId,
23
+ instanceId,
24
+ handler,
25
+ lastPollAt: Date.now(),
26
+ });
27
+ return subId;
28
+ }
29
+ /** Unsubscribe from a trigger. */
30
+ unsubscribe(subscriptionId) {
31
+ return this.subscriptions.delete(subscriptionId);
32
+ }
33
+ /** Poll all subscriptions and invoke handlers for new events. */
34
+ async pollAll() {
35
+ const allEvents = [];
36
+ for (const [subId, sub] of this.subscriptions) {
37
+ const connector = this.registry.get(sub.connectorId);
38
+ if (!connector?.pollTrigger)
39
+ continue;
40
+ const token = this.authManager.getToken(sub.instanceId);
41
+ if (!token)
42
+ continue;
43
+ try {
44
+ const events = await connector.pollTrigger(sub.triggerId, token.accessToken, sub.lastPollAt);
45
+ if (events.length > 0) {
46
+ await sub.handler(events);
47
+ allEvents.push(...events);
48
+ }
49
+ sub.lastPollAt = Date.now();
50
+ }
51
+ catch {
52
+ // Poll errors are silently ignored to avoid breaking other subscriptions
53
+ }
54
+ }
55
+ return allEvents;
56
+ }
57
+ /** Get all active subscription IDs. */
58
+ getSubscriptions() {
59
+ return [...this.subscriptions.keys()];
60
+ }
61
+ }
62
+ //# sourceMappingURL=trigger-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trigger-manager.js","sourceRoot":"","sources":["../src/trigger-manager.ts"],"names":[],"mappings":"AAcA,MAAM,OAAO,cAAc;IACjB,QAAQ,CAAoB;IAC5B,WAAW,CAAc;IACzB,aAAa,GAAG,IAAI,GAAG,EAAwB,CAAC;IAExD,YAAY,QAA2B,EAAE,WAAwB;QAC/D,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;IAED,iFAAiF;IACjF,SAAS,CACP,WAAmB,EACnB,SAAiB,EACjB,UAAkB,EAClB,OAAuB;QAEvB,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACjD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,cAAc,WAAW,aAAa,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,OAAO,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;QACnE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,YAAY,SAAS,6BAA6B,WAAW,GAAG,CAAC,CAAC;QACpF,CAAC;QAED,MAAM,KAAK,GAAG,GAAG,WAAW,IAAI,SAAS,IAAI,UAAU,EAAE,CAAC;QAC1D,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE;YAC5B,WAAW;YACX,SAAS;YACT,UAAU;YACV,OAAO;YACP,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;SACvB,CAAC,CAAC;QAEH,OAAO,KAAK,CAAC;IACf,CAAC;IAED,kCAAkC;IAClC,WAAW,CAAC,cAAsB;QAChC,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IACnD,CAAC;IAED,iEAAiE;IACjE,KAAK,CAAC,OAAO;QACX,MAAM,SAAS,GAAmB,EAAE,CAAC;QAErC,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACrD,IAAI,CAAC,SAAS,EAAE,WAAW;gBAAE,SAAS;YAEtC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACxD,IAAI,CAAC,KAAK;gBAAE,SAAS;YAErB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,WAAW,CACxC,GAAG,CAAC,SAAS,EACb,KAAK,CAAC,WAAW,EACjB,GAAG,CAAC,UAAU,CACf,CAAC;gBAEF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACtB,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;oBAC1B,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;gBAC5B,CAAC;gBAED,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,yEAAyE;YAC3E,CAAC;QACH,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,uCAAuC;IACvC,gBAAgB;QACd,OAAO,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;CACF"}
@@ -0,0 +1,101 @@
1
+ import type { TrustLevel, TrustDomain } from '@auxiora/autonomy';
2
+ /** Supported authentication methods for connectors. */
3
+ export type AuthType = 'oauth2' | 'api_key' | 'token';
4
+ /** OAuth2 configuration for connectors that use OAuth2 auth. */
5
+ export interface OAuth2Config {
6
+ authUrl: string;
7
+ tokenUrl: string;
8
+ scopes: string[];
9
+ clientId?: string;
10
+ clientSecret?: string;
11
+ }
12
+ /** Authentication configuration for a connector. */
13
+ export interface AuthConfig {
14
+ type: AuthType;
15
+ oauth2?: OAuth2Config;
16
+ /** Human-readable instructions for obtaining credentials. */
17
+ instructions?: string;
18
+ }
19
+ /** Defines a single action that a connector can perform. */
20
+ export interface ActionDefinition {
21
+ id: string;
22
+ name: string;
23
+ description: string;
24
+ /** Minimum trust level required to execute this action. */
25
+ trustMinimum: TrustLevel;
26
+ /** Trust domain this action operates in. */
27
+ trustDomain: TrustDomain;
28
+ /** Whether this action can be reversed/undone. */
29
+ reversible: boolean;
30
+ /** Whether this action has external side effects. */
31
+ sideEffects: boolean;
32
+ /** Parameter schema (JSON Schema-like). */
33
+ params: Record<string, ParamDefinition>;
34
+ }
35
+ /** Parameter definition for an action. */
36
+ export interface ParamDefinition {
37
+ type: 'string' | 'number' | 'boolean' | 'object' | 'array';
38
+ description: string;
39
+ required?: boolean;
40
+ default?: unknown;
41
+ }
42
+ /** Defines a trigger that a connector can listen for. */
43
+ export interface TriggerDefinition {
44
+ id: string;
45
+ name: string;
46
+ description: string;
47
+ /** How this trigger is detected. */
48
+ type: 'poll' | 'webhook';
49
+ /** Polling interval in ms (for poll triggers). */
50
+ pollIntervalMs?: number;
51
+ }
52
+ /** Defines an entity type that a connector exposes. */
53
+ export interface EntityDefinition {
54
+ id: string;
55
+ name: string;
56
+ description: string;
57
+ /** Fields exposed by this entity. */
58
+ fields: Record<string, string>;
59
+ }
60
+ /** Full connector definition. */
61
+ export interface Connector {
62
+ id: string;
63
+ name: string;
64
+ description: string;
65
+ version: string;
66
+ category: string;
67
+ icon?: string;
68
+ auth: AuthConfig;
69
+ actions: ActionDefinition[];
70
+ triggers: TriggerDefinition[];
71
+ entities: EntityDefinition[];
72
+ /** Action handler called to execute actions. */
73
+ executeAction: (actionId: string, params: Record<string, unknown>, token: string) => Promise<unknown>;
74
+ /** Trigger poll handler. */
75
+ pollTrigger?: (triggerId: string, token: string, lastPollAt?: number) => Promise<TriggerEvent[]>;
76
+ }
77
+ /** Event emitted by a trigger. */
78
+ export interface TriggerEvent {
79
+ triggerId: string;
80
+ connectorId: string;
81
+ data: Record<string, unknown>;
82
+ timestamp: number;
83
+ }
84
+ /** Stored token data for a connector instance. */
85
+ export interface StoredToken {
86
+ accessToken: string;
87
+ refreshToken?: string;
88
+ expiresAt?: number;
89
+ tokenType?: string;
90
+ scopes?: string[];
91
+ }
92
+ /** A configured instance of a connector with auth credentials. */
93
+ export interface ConnectorInstance {
94
+ id: string;
95
+ connectorId: string;
96
+ label: string;
97
+ createdAt: number;
98
+ /** Whether auth is configured and valid. */
99
+ authenticated: boolean;
100
+ }
101
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@auxiora/connectors",
3
+ "version": "1.0.0",
4
+ "description": "Connector SDK, registry, auth manager, and action executor",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "dependencies": {
15
+ "@auxiora/autonomy": "1.0.0",
16
+ "@auxiora/core": "1.0.0"
17
+ },
18
+ "engines": {
19
+ "node": ">=22.0.0"
20
+ },
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "clean": "rm -rf dist",
24
+ "typecheck": "tsc --noEmit"
25
+ }
26
+ }
@@ -0,0 +1,177 @@
1
+ import type { AuthConfig, StoredToken } from './types.js';
2
+
3
+ const GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token';
4
+
5
+ export interface AuthManagerVault {
6
+ get(name: string): string | undefined;
7
+ has(name: string): boolean;
8
+ add(name: string, value: string): Promise<void>;
9
+ }
10
+
11
+ /** In-memory token store with vault-like interface for connector auth tokens. */
12
+ export class AuthManager {
13
+ private tokens = new Map<string, StoredToken>();
14
+ private vault?: AuthManagerVault;
15
+
16
+ constructor(vault?: AuthManagerVault) {
17
+ this.vault = vault;
18
+ }
19
+
20
+ /** Authenticate a connector instance with the given credentials. */
21
+ async authenticate(
22
+ instanceId: string,
23
+ authConfig: AuthConfig,
24
+ credentials: Record<string, string>,
25
+ ): Promise<StoredToken> {
26
+ let token: StoredToken;
27
+
28
+ switch (authConfig.type) {
29
+ case 'oauth2': {
30
+ if (!credentials.accessToken) {
31
+ throw new Error('OAuth2 authentication requires an accessToken');
32
+ }
33
+ token = {
34
+ accessToken: credentials.accessToken,
35
+ refreshToken: credentials.refreshToken,
36
+ expiresAt: credentials.expiresAt ? parseInt(credentials.expiresAt, 10) : undefined,
37
+ tokenType: credentials.tokenType ?? 'Bearer',
38
+ scopes: authConfig.oauth2?.scopes,
39
+ };
40
+ break;
41
+ }
42
+ case 'api_key': {
43
+ if (!credentials.apiKey) {
44
+ throw new Error('API key authentication requires an apiKey');
45
+ }
46
+ token = {
47
+ accessToken: credentials.apiKey,
48
+ tokenType: 'api_key',
49
+ };
50
+ break;
51
+ }
52
+ case 'token': {
53
+ if (!credentials.token) {
54
+ throw new Error('Token authentication requires a token');
55
+ }
56
+ token = {
57
+ accessToken: credentials.token,
58
+ tokenType: 'Bearer',
59
+ };
60
+ break;
61
+ }
62
+ default:
63
+ throw new Error(`Unsupported auth type: ${authConfig.type as string}`);
64
+ }
65
+
66
+ this.tokens.set(instanceId, token);
67
+
68
+ // Persist tokens to vault if available
69
+ if (this.vault) {
70
+ await this.vault.add(`connectors.${instanceId}.tokens`, JSON.stringify(token));
71
+ }
72
+
73
+ return token;
74
+ }
75
+
76
+ /** Refresh an OAuth2 token for a connector instance. */
77
+ async refreshToken(
78
+ instanceId: string,
79
+ authConfig: AuthConfig,
80
+ ): Promise<StoredToken> {
81
+ const existing = this.tokens.get(instanceId);
82
+ if (!existing) {
83
+ throw new Error(`No token found for instance "${instanceId}"`);
84
+ }
85
+ if (authConfig.type !== 'oauth2') {
86
+ throw new Error('Token refresh is only supported for OAuth2');
87
+ }
88
+ if (!existing.refreshToken) {
89
+ throw new Error('No refresh token available');
90
+ }
91
+
92
+ // Load client credentials from vault
93
+ const credsJson = this.vault?.get(`connectors.${instanceId}.credentials`);
94
+ if (!credsJson) {
95
+ throw new Error(`No client credentials found for instance "${instanceId}"`);
96
+ }
97
+ const creds = JSON.parse(credsJson) as { clientId: string; clientSecret: string };
98
+
99
+ // Call Google's token endpoint to refresh
100
+ const body = new URLSearchParams({
101
+ client_id: creds.clientId,
102
+ client_secret: creds.clientSecret,
103
+ refresh_token: existing.refreshToken,
104
+ grant_type: 'refresh_token',
105
+ });
106
+
107
+ const response = await fetch(GOOGLE_TOKEN_URL, {
108
+ method: 'POST',
109
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
110
+ body: body.toString(),
111
+ });
112
+
113
+ if (!response.ok) {
114
+ const errBody = await response.text();
115
+ throw new Error(`Token refresh failed: ${response.status} ${errBody}`);
116
+ }
117
+
118
+ const data = await response.json() as {
119
+ access_token: string;
120
+ refresh_token?: string;
121
+ expires_in?: number;
122
+ token_type?: string;
123
+ };
124
+
125
+ const refreshed: StoredToken = {
126
+ accessToken: data.access_token,
127
+ refreshToken: data.refresh_token ?? existing.refreshToken,
128
+ expiresAt: data.expires_in ? Date.now() + data.expires_in * 1000 : Date.now() + 3600_000,
129
+ tokenType: data.token_type ?? 'Bearer',
130
+ scopes: existing.scopes,
131
+ };
132
+
133
+ this.tokens.set(instanceId, refreshed);
134
+
135
+ // Persist updated tokens to vault
136
+ if (this.vault) {
137
+ await this.vault.add(`connectors.${instanceId}.tokens`, JSON.stringify(refreshed));
138
+ }
139
+
140
+ return refreshed;
141
+ }
142
+
143
+ /** Restore a token from vault into the in-memory store. */
144
+ restoreToken(instanceId: string, token: StoredToken): void {
145
+ this.tokens.set(instanceId, token);
146
+ }
147
+
148
+ /** Get the stored token for a connector instance. */
149
+ getToken(instanceId: string): StoredToken | undefined {
150
+ return this.tokens.get(instanceId);
151
+ }
152
+
153
+ /** Check if a token is expired. */
154
+ isTokenExpired(instanceId: string): boolean {
155
+ const token = this.tokens.get(instanceId);
156
+ if (!token || !token.expiresAt) return false;
157
+ return Date.now() >= token.expiresAt;
158
+ }
159
+
160
+ /** Revoke and remove a token for a connector instance. */
161
+ async revokeToken(instanceId: string): Promise<boolean> {
162
+ const deleted = this.tokens.delete(instanceId);
163
+ if (deleted && this.vault) {
164
+ try {
165
+ await this.vault.add(`connectors.${instanceId}.tokens`, '');
166
+ } catch {
167
+ // Best-effort vault cleanup
168
+ }
169
+ }
170
+ return deleted;
171
+ }
172
+
173
+ /** Check whether a connector instance has a stored token. */
174
+ hasToken(instanceId: string): boolean {
175
+ return this.tokens.has(instanceId);
176
+ }
177
+ }
@@ -0,0 +1,45 @@
1
+ import type { Connector, ActionDefinition, TriggerDefinition, EntityDefinition, AuthConfig, TriggerEvent } from './types.js';
2
+
3
+ /** Options for defining a connector via the SDK helper. */
4
+ export interface DefineConnectorOptions {
5
+ id: string;
6
+ name: string;
7
+ description: string;
8
+ version: string;
9
+ category: string;
10
+ icon?: string;
11
+ auth: AuthConfig;
12
+ actions: ActionDefinition[];
13
+ triggers?: TriggerDefinition[];
14
+ entities?: EntityDefinition[];
15
+ executeAction: (actionId: string, params: Record<string, unknown>, token: string) => Promise<unknown>;
16
+ pollTrigger?: (triggerId: string, token: string, lastPollAt?: number) => Promise<TriggerEvent[]>;
17
+ }
18
+
19
+ /** SDK helper to define a connector with validated structure. */
20
+ export function defineConnector(options: DefineConnectorOptions): Connector {
21
+ if (!options.id || !options.name) {
22
+ throw new Error('Connector id and name are required');
23
+ }
24
+ if (!options.auth) {
25
+ throw new Error('Connector auth config is required');
26
+ }
27
+ if (!options.actions || options.actions.length === 0) {
28
+ throw new Error('Connector must define at least one action');
29
+ }
30
+
31
+ return {
32
+ id: options.id,
33
+ name: options.name,
34
+ description: options.description,
35
+ version: options.version,
36
+ category: options.category,
37
+ icon: options.icon,
38
+ auth: options.auth,
39
+ actions: options.actions,
40
+ triggers: options.triggers ?? [],
41
+ entities: options.entities ?? [],
42
+ executeAction: options.executeAction,
43
+ pollTrigger: options.pollTrigger,
44
+ };
45
+ }
@@ -0,0 +1,108 @@
1
+ import type { TrustGate } from '@auxiora/autonomy';
2
+ import type { ActionAuditTrail } from '@auxiora/autonomy';
3
+ import type { ConnectorRegistry } from './registry.js';
4
+ import type { AuthManager } from './auth-manager.js';
5
+ import type { TrustDomain, TrustLevel } from '@auxiora/autonomy';
6
+
7
+ export interface ExecutionResult {
8
+ success: boolean;
9
+ data?: unknown;
10
+ error?: string;
11
+ auditId?: string;
12
+ }
13
+
14
+ export class ActionExecutor {
15
+ private registry: ConnectorRegistry;
16
+ private authManager: AuthManager;
17
+ private trustGate: TrustGate;
18
+ private auditTrail: ActionAuditTrail;
19
+
20
+ constructor(
21
+ registry: ConnectorRegistry,
22
+ authManager: AuthManager,
23
+ trustGate: TrustGate,
24
+ auditTrail: ActionAuditTrail,
25
+ ) {
26
+ this.registry = registry;
27
+ this.authManager = authManager;
28
+ this.trustGate = trustGate;
29
+ this.auditTrail = auditTrail;
30
+ }
31
+
32
+ async execute(
33
+ connectorId: string,
34
+ actionId: string,
35
+ params: Record<string, unknown>,
36
+ instanceId: string,
37
+ ): Promise<ExecutionResult> {
38
+ const connector = this.registry.get(connectorId);
39
+ if (!connector) {
40
+ return { success: false, error: `Connector "${connectorId}" not found` };
41
+ }
42
+
43
+ const action = connector.actions.find((a) => a.id === actionId);
44
+ if (!action) {
45
+ return { success: false, error: `Action "${actionId}" not found in connector "${connectorId}"` };
46
+ }
47
+
48
+ // Check trust gate
49
+ const gateResult = this.trustGate.gate(
50
+ action.trustDomain,
51
+ `${connectorId}:${actionId}`,
52
+ action.trustMinimum,
53
+ );
54
+
55
+ if (!gateResult.allowed) {
56
+ const audit = await this.auditTrail.record({
57
+ trustLevel: gateResult.currentLevel,
58
+ domain: action.trustDomain,
59
+ intent: `${connectorId}:${actionId}`,
60
+ plan: JSON.stringify(params),
61
+ executed: false,
62
+ outcome: 'failure',
63
+ reasoning: gateResult.message,
64
+ rollbackAvailable: false,
65
+ });
66
+ return { success: false, error: gateResult.message, auditId: audit.id };
67
+ }
68
+
69
+ // Get token
70
+ const token = this.authManager.getToken(instanceId);
71
+ if (!token) {
72
+ return { success: false, error: `No authentication token for instance "${instanceId}"` };
73
+ }
74
+
75
+ // Execute
76
+ try {
77
+ const data = await connector.executeAction(actionId, params, token.accessToken);
78
+
79
+ const audit = await this.auditTrail.record({
80
+ trustLevel: gateResult.currentLevel,
81
+ domain: action.trustDomain,
82
+ intent: `${connectorId}:${actionId}`,
83
+ plan: JSON.stringify(params),
84
+ executed: true,
85
+ outcome: 'success',
86
+ reasoning: gateResult.message,
87
+ rollbackAvailable: action.reversible,
88
+ });
89
+
90
+ return { success: true, data, auditId: audit.id };
91
+ } catch (error) {
92
+ const msg = error instanceof Error ? error.message : 'Unknown error';
93
+
94
+ const audit = await this.auditTrail.record({
95
+ trustLevel: gateResult.currentLevel,
96
+ domain: action.trustDomain,
97
+ intent: `${connectorId}:${actionId}`,
98
+ plan: JSON.stringify(params),
99
+ executed: true,
100
+ outcome: 'failure',
101
+ reasoning: msg,
102
+ rollbackAvailable: false,
103
+ });
104
+
105
+ return { success: false, error: msg, auditId: audit.id };
106
+ }
107
+ }
108
+ }
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
1
+ export type {
2
+ AuthType,
3
+ OAuth2Config,
4
+ AuthConfig,
5
+ ActionDefinition,
6
+ ParamDefinition,
7
+ TriggerDefinition,
8
+ EntityDefinition,
9
+ Connector,
10
+ TriggerEvent,
11
+ StoredToken,
12
+ ConnectorInstance,
13
+ } from './types.js';
14
+ export { ConnectorRegistry } from './registry.js';
15
+ export { AuthManager } from './auth-manager.js';
16
+ export { ActionExecutor, type ExecutionResult } from './executor.js';
17
+ export { TriggerManager, type TriggerHandler } from './trigger-manager.js';
18
+ export { defineConnector, type DefineConnectorOptions } from './define-connector.js';
@@ -0,0 +1,42 @@
1
+ import type { Connector, ActionDefinition, TriggerDefinition } from './types.js';
2
+
3
+ export class ConnectorRegistry {
4
+ private connectors = new Map<string, Connector>();
5
+
6
+ register(connector: Connector): void {
7
+ if (this.connectors.has(connector.id)) {
8
+ throw new Error(`Connector "${connector.id}" is already registered`);
9
+ }
10
+ this.connectors.set(connector.id, connector);
11
+ }
12
+
13
+ get(id: string): Connector | undefined {
14
+ return this.connectors.get(id);
15
+ }
16
+
17
+ list(): Connector[] {
18
+ return [...this.connectors.values()];
19
+ }
20
+
21
+ listByCategory(category: string): Connector[] {
22
+ return [...this.connectors.values()].filter((c) => c.category === category);
23
+ }
24
+
25
+ getActions(connectorId: string): ActionDefinition[] {
26
+ const connector = this.connectors.get(connectorId);
27
+ return connector ? connector.actions : [];
28
+ }
29
+
30
+ getTriggers(connectorId: string): TriggerDefinition[] {
31
+ const connector = this.connectors.get(connectorId);
32
+ return connector ? connector.triggers : [];
33
+ }
34
+
35
+ has(id: string): boolean {
36
+ return this.connectors.has(id);
37
+ }
38
+
39
+ unregister(id: string): boolean {
40
+ return this.connectors.delete(id);
41
+ }
42
+ }