@friggframework/admin-scripts 2.0.0--canary.517.8eaf5df.0 → 2.0.0--canary.517.ff03f2c.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.
@@ -1,311 +0,0 @@
1
- const { AdminScriptBase } = require('../application/admin-script-base');
2
-
3
- /**
4
- * Integration Health Check Script
5
- *
6
- * Checks the health of integrations by verifying:
7
- * - Credential validity
8
- * - API connectivity
9
- * - Configuration integrity
10
- */
11
- class IntegrationHealthCheckScript extends AdminScriptBase {
12
- static Definition = {
13
- name: 'integration-health-check',
14
- version: '1.0.0',
15
- description: 'Checks health of integrations and reports issues',
16
- source: 'BUILTIN',
17
-
18
- inputSchema: {
19
- type: 'object',
20
- properties: {
21
- integrationIds: {
22
- type: 'array',
23
- items: { type: 'string' },
24
- description:
25
- 'Specific integration IDs to check (optional, defaults to all)',
26
- },
27
- checkCredentials: {
28
- type: 'boolean',
29
- default: true,
30
- description: 'Verify credential validity',
31
- },
32
- checkConnectivity: {
33
- type: 'boolean',
34
- default: true,
35
- description: 'Test API connectivity',
36
- },
37
- updateStatus: {
38
- type: 'boolean',
39
- default: false,
40
- description: 'Update integration status based on health',
41
- },
42
- },
43
- },
44
-
45
- outputSchema: {
46
- type: 'object',
47
- properties: {
48
- healthy: { type: 'number' },
49
- unhealthy: { type: 'number' },
50
- unknown: { type: 'number' },
51
- results: { type: 'array' },
52
- },
53
- },
54
-
55
- config: {
56
- timeout: 900000, // 15 minutes
57
- requireIntegrationInstance: true,
58
- },
59
-
60
- schedule: {
61
- enabled: false, // Can be enabled via API
62
- cronExpression: 'cron(0 6 * * ? *)', // Daily at 6 AM UTC
63
- },
64
-
65
- // UI-specific overrides
66
- display: {
67
- category: 'maintenance',
68
- },
69
- };
70
-
71
- async execute(params = {}) {
72
- const {
73
- integrationIds = null,
74
- checkCredentials = true,
75
- checkConnectivity = true,
76
- updateStatus = false,
77
- } = params;
78
-
79
- const summary = {
80
- healthy: 0,
81
- unhealthy: 0,
82
- unknown: 0,
83
- results: [],
84
- };
85
-
86
- this.context.log('info', 'Starting integration health check', {
87
- checkCredentials,
88
- checkConnectivity,
89
- updateStatus,
90
- specificIds: integrationIds?.length || 'all',
91
- });
92
-
93
- // Get integrations to check
94
- let integrations;
95
- if (integrationIds && integrationIds.length > 0) {
96
- integrations = await Promise.all(
97
- integrationIds.map((id) =>
98
- this.context.integrationRepository
99
- .findIntegrationById(id)
100
- .catch(() => null)
101
- )
102
- );
103
- integrations = integrations.filter(Boolean);
104
- } else {
105
- integrations = await this.getAllIntegrations();
106
- }
107
-
108
- this.context.log(
109
- 'info',
110
- `Checking ${integrations.length} integrations`
111
- );
112
-
113
- for (const integration of integrations) {
114
- const result = await this.checkIntegration(integration, {
115
- checkCredentials,
116
- checkConnectivity,
117
- });
118
-
119
- summary.results.push(result);
120
-
121
- if (result.status === 'healthy') {
122
- summary.healthy++;
123
- } else if (result.status === 'unhealthy') {
124
- summary.unhealthy++;
125
- } else {
126
- summary.unknown++;
127
- }
128
-
129
- // Optionally update integration status
130
- if (updateStatus && result.status !== 'unknown') {
131
- try {
132
- const newStatus =
133
- result.status === 'healthy' ? 'ACTIVE' : 'ERROR';
134
- await this.context.integrationRepository.updateIntegrationStatus(
135
- integration.id,
136
- newStatus
137
- );
138
- this.context.log(
139
- 'info',
140
- `Updated status for ${integration.id} to ${newStatus}`
141
- );
142
- } catch (error) {
143
- this.context.log(
144
- 'warn',
145
- `Failed to update status for ${integration.id}`,
146
- {
147
- error: error.message,
148
- }
149
- );
150
- }
151
- }
152
- }
153
-
154
- this.context.log('info', 'Health check completed', {
155
- healthy: summary.healthy,
156
- unhealthy: summary.unhealthy,
157
- unknown: summary.unknown,
158
- });
159
-
160
- return summary;
161
- }
162
-
163
- async getAllIntegrations() {
164
- return this.context.integrationRepository.findIntegrations({});
165
- }
166
-
167
- async checkIntegration(integration, options) {
168
- const { checkCredentials, checkConnectivity } = options;
169
- const result = this._createCheckResult(integration);
170
-
171
- try {
172
- await this._runChecks(integration, result, {
173
- checkCredentials,
174
- checkConnectivity,
175
- });
176
- this._determineOverallStatus(result);
177
- } catch (error) {
178
- this._handleCheckError(integration, result, error);
179
- }
180
-
181
- return result;
182
- }
183
-
184
- /**
185
- * Create initial check result object
186
- * @private
187
- */
188
- _createCheckResult(integration) {
189
- return {
190
- integrationId: integration.id,
191
- integrationType: integration.config?.type || 'unknown',
192
- status: 'unknown',
193
- checks: {},
194
- issues: [],
195
- };
196
- }
197
-
198
- /**
199
- * Run all requested checks
200
- * @private
201
- */
202
- async _runChecks(integration, result, options) {
203
- const { checkCredentials, checkConnectivity } = options;
204
-
205
- if (checkCredentials) {
206
- this._addCheckResult(
207
- result,
208
- 'credentials',
209
- this.checkCredentialValidity(integration)
210
- );
211
- }
212
-
213
- if (checkConnectivity) {
214
- this._addCheckResult(
215
- result,
216
- 'connectivity',
217
- await this.checkApiConnectivity(integration)
218
- );
219
- }
220
- }
221
-
222
- /**
223
- * Add a check result and track any issues
224
- * @private
225
- */
226
- _addCheckResult(result, checkName, checkResult) {
227
- result.checks[checkName] = checkResult;
228
- if (!checkResult.valid) {
229
- result.issues.push(checkResult.issue);
230
- }
231
- }
232
-
233
- /**
234
- * Determine overall health status from issues
235
- * @private
236
- */
237
- _determineOverallStatus(result) {
238
- result.status = result.issues.length === 0 ? 'healthy' : 'unhealthy';
239
- }
240
-
241
- /**
242
- * Handle check error and update result
243
- * @private
244
- */
245
- _handleCheckError(integration, result, error) {
246
- this.context.log(
247
- 'error',
248
- `Error checking integration ${integration.id}`,
249
- {
250
- error: error.message,
251
- }
252
- );
253
- result.status = 'unknown';
254
- result.issues.push(`Check failed: ${error.message}`);
255
- }
256
-
257
- checkCredentialValidity(integration) {
258
- const result = { valid: true, issue: null };
259
-
260
- // Check for access token
261
- if (!integration.config?.credentials?.access_token) {
262
- result.valid = false;
263
- result.issue = 'Missing access token';
264
- return result;
265
- }
266
-
267
- // Check for expiry
268
- const expiresAt = integration.config?.credentials?.expires_at;
269
- if (expiresAt) {
270
- const expiryTime = new Date(expiresAt);
271
- if (expiryTime < new Date()) {
272
- result.valid = false;
273
- result.issue = 'Access token expired';
274
- return result;
275
- }
276
- }
277
-
278
- return result;
279
- }
280
-
281
- async checkApiConnectivity(integration) {
282
- const result = { valid: true, issue: null, responseTime: null };
283
-
284
- try {
285
- const startTime = Date.now();
286
- const instance = await this.context.instantiate(integration.id);
287
-
288
- // Try to make a simple API call
289
- if (instance.primary?.api?.getAuthenticationInfo) {
290
- await instance.primary.api.getAuthenticationInfo();
291
- } else if (instance.primary?.api?.getCurrentUser) {
292
- await instance.primary.api.getCurrentUser();
293
- } else {
294
- // No suitable health check method
295
- result.valid = true;
296
- result.issue = null;
297
- result.note = 'No health check endpoint available';
298
- return result;
299
- }
300
-
301
- result.responseTime = Date.now() - startTime;
302
- } catch (error) {
303
- result.valid = false;
304
- result.issue = `API connectivity failed: ${error.message}`;
305
- }
306
-
307
- return result;
308
- }
309
- }
310
-
311
- module.exports = { IntegrationHealthCheckScript };
@@ -1,255 +0,0 @@
1
- const { AdminScriptBase } = require('../application/admin-script-base');
2
-
3
- /**
4
- * OAuth Token Refresh Script
5
- *
6
- * Refreshes OAuth tokens for integrations that are near expiry.
7
- * This helps prevent authentication failures due to expired tokens.
8
- */
9
- class OAuthTokenRefreshScript extends AdminScriptBase {
10
- static Definition = {
11
- name: 'oauth-token-refresh',
12
- version: '1.0.0',
13
- description: 'Refreshes OAuth tokens for integrations near expiry',
14
- source: 'BUILTIN',
15
-
16
- inputSchema: {
17
- type: 'object',
18
- properties: {
19
- integrationIds: {
20
- type: 'array',
21
- items: { type: 'string' },
22
- description:
23
- 'Specific integration IDs to refresh (optional, defaults to all)',
24
- },
25
- expiryThresholdHours: {
26
- type: 'number',
27
- default: 24,
28
- description:
29
- 'Refresh tokens expiring within this many hours',
30
- },
31
- dryRun: {
32
- type: 'boolean',
33
- default: false,
34
- description: 'Preview without making changes',
35
- },
36
- },
37
- },
38
-
39
- outputSchema: {
40
- type: 'object',
41
- properties: {
42
- refreshed: { type: 'number' },
43
- failed: { type: 'number' },
44
- skipped: { type: 'number' },
45
- details: { type: 'array' },
46
- },
47
- },
48
-
49
- config: {
50
- timeout: 600000, // 10 minutes
51
- requireIntegrationInstance: true, // Needs to call external APIs
52
- },
53
-
54
- // UI-specific overrides
55
- display: {
56
- category: 'maintenance',
57
- },
58
- };
59
-
60
- async execute(params = {}) {
61
- const {
62
- integrationIds = null,
63
- expiryThresholdHours = 24,
64
- dryRun = false,
65
- } = params;
66
-
67
- const results = {
68
- refreshed: 0,
69
- failed: 0,
70
- skipped: 0,
71
- details: [],
72
- };
73
-
74
- this.context.log('info', 'Starting OAuth token refresh', {
75
- expiryThresholdHours,
76
- dryRun,
77
- specificIds: integrationIds?.length || 'all',
78
- });
79
-
80
- // Get integrations to check
81
- let integrations;
82
- if (integrationIds && integrationIds.length > 0) {
83
- integrations = await Promise.all(
84
- integrationIds.map((id) =>
85
- this.context.integrationRepository
86
- .findIntegrationById(id)
87
- .catch(() => null)
88
- )
89
- );
90
- integrations = integrations.filter(Boolean);
91
- } else {
92
- // Get all integrations (this would need to be paginated for large deployments)
93
- integrations = await this.getAllIntegrations();
94
- }
95
-
96
- this.context.log(
97
- 'info',
98
- `Found ${integrations.length} integrations to check`
99
- );
100
-
101
- for (const integration of integrations) {
102
- try {
103
- const detail = await this.processIntegration(integration, {
104
- expiryThresholdHours,
105
- dryRun,
106
- });
107
-
108
- results.details.push(detail);
109
-
110
- if (detail.action === 'refreshed') {
111
- results.refreshed++;
112
- } else if (detail.action === 'skipped') {
113
- results.skipped++;
114
- } else if (detail.action === 'failed') {
115
- results.failed++;
116
- }
117
- } catch (error) {
118
- this.context.log(
119
- 'error',
120
- `Error processing integration ${integration.id}`,
121
- {
122
- error: error.message,
123
- }
124
- );
125
- results.failed++;
126
- results.details.push({
127
- integrationId: integration.id,
128
- action: 'failed',
129
- reason: error.message,
130
- });
131
- }
132
- }
133
-
134
- this.context.log('info', 'OAuth token refresh completed', {
135
- refreshed: results.refreshed,
136
- failed: results.failed,
137
- skipped: results.skipped,
138
- });
139
-
140
- return results;
141
- }
142
-
143
- async getAllIntegrations() {
144
- // This is a simplified implementation
145
- // In production, would need pagination for large datasets
146
- return this.context.integrationRepository.findIntegrations({});
147
- }
148
-
149
- async processIntegration(integration, options) {
150
- const { expiryThresholdHours, dryRun } = options;
151
-
152
- // Check prerequisites
153
- const skipReason = this._checkRefreshPrerequisites(
154
- integration,
155
- expiryThresholdHours
156
- );
157
- if (skipReason) {
158
- return this._createResult(integration.id, 'skipped', skipReason);
159
- }
160
-
161
- // Handle dry run
162
- if (dryRun) {
163
- this.context.log(
164
- 'info',
165
- `[DRY RUN] Would refresh token for ${integration.id}`
166
- );
167
- return this._createResult(
168
- integration.id,
169
- 'skipped',
170
- 'Dry run - would have refreshed'
171
- );
172
- }
173
-
174
- // Perform refresh
175
- return this._performTokenRefresh(integration);
176
- }
177
-
178
- /**
179
- * Check if integration meets prerequisites for token refresh
180
- * @private
181
- * @returns {string|null} Skip reason or null if eligible
182
- */
183
- _checkRefreshPrerequisites(integration, expiryThresholdHours) {
184
- if (!integration.config?.credentials?.access_token) {
185
- return 'No OAuth credentials found';
186
- }
187
-
188
- const expiresAt = integration.config?.credentials?.expires_at;
189
- if (!expiresAt) {
190
- return 'No expiry time found';
191
- }
192
-
193
- const expiryTime = new Date(expiresAt);
194
- const thresholdTime = new Date(
195
- Date.now() + expiryThresholdHours * 60 * 60 * 1000
196
- );
197
-
198
- if (expiryTime > thresholdTime) {
199
- return 'Token not near expiry';
200
- }
201
-
202
- return null;
203
- }
204
-
205
- /**
206
- * Perform the actual token refresh
207
- * @private
208
- */
209
- async _performTokenRefresh(integration) {
210
- const expiresAt = integration.config?.credentials?.expires_at;
211
-
212
- try {
213
- const instance = await this.context.instantiate(integration.id);
214
-
215
- if (!instance.primary?.api?.refreshAccessToken) {
216
- return this._createResult(
217
- integration.id,
218
- 'skipped',
219
- 'API does not support token refresh'
220
- );
221
- }
222
-
223
- await instance.primary.api.refreshAccessToken();
224
- this.context.log(
225
- 'info',
226
- `Refreshed token for integration ${integration.id}`
227
- );
228
-
229
- return {
230
- integrationId: integration.id,
231
- action: 'refreshed',
232
- previousExpiry: expiresAt,
233
- };
234
- } catch (error) {
235
- this.context.log(
236
- 'error',
237
- `Failed to refresh token for ${integration.id}`,
238
- {
239
- error: error.message,
240
- }
241
- );
242
- return this._createResult(integration.id, 'failed', error.message);
243
- }
244
- }
245
-
246
- /**
247
- * Create a result object
248
- * @private
249
- */
250
- _createResult(integrationId, action, reason) {
251
- return { integrationId, action, reason };
252
- }
253
- }
254
-
255
- module.exports = { OAuthTokenRefreshScript };