@friggframework/admin-scripts 2.0.0--canary.517.f04156f.0 → 2.0.0--canary.517.300ded3.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.
package/index.js CHANGED
@@ -12,7 +12,7 @@ const { AdminFriggCommands, createAdminFriggCommands } = require('./src/applicat
12
12
  const { ScriptRunner, createScriptRunner } = require('./src/application/script-runner');
13
13
 
14
14
  // Infrastructure
15
- const { adminAuthMiddleware } = require('./src/infrastructure/admin-auth-middleware');
15
+ const { validateAdminApiKey } = require('./src/infrastructure/admin-auth-middleware');
16
16
  const { router, app, handler: routerHandler } = require('./src/infrastructure/admin-script-router');
17
17
  const { handler: executorHandler } = require('./src/infrastructure/script-executor-handler');
18
18
 
@@ -45,7 +45,7 @@ module.exports = {
45
45
  createScriptRunner,
46
46
 
47
47
  // Infrastructure layer
48
- adminAuthMiddleware,
48
+ validateAdminApiKey,
49
49
  router,
50
50
  app,
51
51
  routerHandler,
package/package.json CHANGED
@@ -1,23 +1,21 @@
1
1
  {
2
2
  "name": "@friggframework/admin-scripts",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.517.f04156f.0",
4
+ "version": "2.0.0--canary.517.300ded3.0",
5
5
  "description": "Admin Script Runner for Frigg - Execute maintenance and operational scripts in hosted environments",
6
6
  "dependencies": {
7
7
  "@aws-sdk/client-scheduler": "^3.588.0",
8
- "@friggframework/core": "2.0.0--canary.517.f04156f.0",
8
+ "@friggframework/core": "2.0.0--canary.517.300ded3.0",
9
9
  "bcryptjs": "^2.4.3",
10
10
  "express": "^4.18.2",
11
11
  "lodash": "4.17.21",
12
- "mongoose": "6.11.6",
13
12
  "serverless-http": "^3.2.0",
14
13
  "uuid": "^9.0.1"
15
14
  },
16
15
  "devDependencies": {
17
- "@friggframework/eslint-config": "2.0.0--canary.517.f04156f.0",
18
- "@friggframework/prettier-config": "2.0.0--canary.517.f04156f.0",
19
- "@friggframework/test": "2.0.0--canary.517.f04156f.0",
20
- "chai": "^4.3.6",
16
+ "@friggframework/eslint-config": "2.0.0--canary.517.300ded3.0",
17
+ "@friggframework/prettier-config": "2.0.0--canary.517.300ded3.0",
18
+ "@friggframework/test": "2.0.0--canary.517.300ded3.0",
21
19
  "eslint": "^8.22.0",
22
20
  "jest": "^29.7.0",
23
21
  "prettier": "^2.7.1",
@@ -49,5 +47,5 @@
49
47
  "maintenance",
50
48
  "operations"
51
49
  ],
52
- "gitHead": "f04156ff92c4e4bec35317ae6eb9cc00987cf395"
50
+ "gitHead": "300ded3ac35558075a081104b4b362b85cf0756f"
53
51
  }
@@ -5,7 +5,7 @@ jest.mock('@friggframework/core/integrations/repositories/integration-repository
5
5
  jest.mock('@friggframework/core/user/repositories/user-repository-factory');
6
6
  jest.mock('@friggframework/core/modules/repositories/module-repository-factory');
7
7
  jest.mock('@friggframework/core/credential/repositories/credential-repository-factory');
8
- jest.mock('@friggframework/core/admin-scripts/repositories/script-execution-repository-factory');
8
+ jest.mock('@friggframework/core/admin-scripts/repositories/admin-process-repository-factory');
9
9
  jest.mock('@friggframework/core/queues');
10
10
 
11
11
  describe('AdminFriggCommands', () => {
@@ -13,7 +13,7 @@ describe('AdminFriggCommands', () => {
13
13
  let mockUserRepo;
14
14
  let mockModuleRepo;
15
15
  let mockCredentialRepo;
16
- let mockScriptExecutionRepo;
16
+ let mockAdminProcessRepo;
17
17
  let mockQueuerUtil;
18
18
 
19
19
  beforeEach(() => {
@@ -46,8 +46,8 @@ describe('AdminFriggCommands', () => {
46
46
  updateCredential: jest.fn(),
47
47
  };
48
48
 
49
- mockScriptExecutionRepo = {
50
- appendExecutionLog: jest.fn().mockResolvedValue(undefined),
49
+ mockAdminProcessRepo = {
50
+ appendProcessLog: jest.fn().mockResolvedValue(undefined),
51
51
  };
52
52
 
53
53
  mockQueuerUtil = {
@@ -60,14 +60,14 @@ describe('AdminFriggCommands', () => {
60
60
  const { createUserRepository } = require('@friggframework/core/user/repositories/user-repository-factory');
61
61
  const { createModuleRepository } = require('@friggframework/core/modules/repositories/module-repository-factory');
62
62
  const { createCredentialRepository } = require('@friggframework/core/credential/repositories/credential-repository-factory');
63
- const { createScriptExecutionRepository } = require('@friggframework/core/admin-scripts/repositories/script-execution-repository-factory');
63
+ const { createAdminProcessRepository } = require('@friggframework/core/admin-scripts/repositories/admin-process-repository-factory');
64
64
  const { QueuerUtil } = require('@friggframework/core/queues');
65
65
 
66
66
  createIntegrationRepository.mockReturnValue(mockIntegrationRepo);
67
67
  createUserRepository.mockReturnValue(mockUserRepo);
68
68
  createModuleRepository.mockReturnValue(mockModuleRepo);
69
69
  createCredentialRepository.mockReturnValue(mockCredentialRepo);
70
- createScriptExecutionRepository.mockReturnValue(mockScriptExecutionRepo);
70
+ createAdminProcessRepository.mockReturnValue(mockAdminProcessRepo);
71
71
 
72
72
  // Mock QueuerUtil methods
73
73
  QueuerUtil.send = mockQueuerUtil.send;
@@ -158,16 +158,16 @@ describe('AdminFriggCommands', () => {
158
158
  expect(repo).toBe(mockCredentialRepo);
159
159
  });
160
160
 
161
- it('creates scriptExecutionRepository on first access', () => {
161
+ it('creates adminProcessRepository on first access', () => {
162
162
  const commands = new AdminFriggCommands();
163
- const { createScriptExecutionRepository } = require('@friggframework/core/admin-scripts/repositories/script-execution-repository-factory');
163
+ const { createAdminProcessRepository } = require('@friggframework/core/admin-scripts/repositories/admin-process-repository-factory');
164
164
 
165
- expect(createScriptExecutionRepository).not.toHaveBeenCalled();
165
+ expect(createAdminProcessRepository).not.toHaveBeenCalled();
166
166
 
167
- const repo = commands.scriptExecutionRepository;
167
+ const repo = commands.adminProcessRepository;
168
168
 
169
- expect(createScriptExecutionRepository).toHaveBeenCalledTimes(1);
170
- expect(repo).toBe(mockScriptExecutionRepo);
169
+ expect(createAdminProcessRepository).toHaveBeenCalledTimes(1);
170
+ expect(repo).toBe(mockAdminProcessRepo);
171
171
  });
172
172
  });
173
173
 
@@ -551,15 +551,15 @@ describe('AdminFriggCommands', () => {
551
551
  it('log() persists if executionId set', async () => {
552
552
  const commands = new AdminFriggCommands({ executionId: 'exec_123' });
553
553
  // Force repository creation
554
- commands.scriptExecutionRepository;
554
+ commands.adminProcessRepository;
555
555
 
556
556
  commands.log('warn', 'Warning message', { detail: 'xyz' });
557
557
 
558
558
  // Give async operation a chance to execute
559
559
  await new Promise(resolve => setImmediate(resolve));
560
560
 
561
- expect(mockScriptExecutionRepo.appendExecutionLog).toHaveBeenCalled();
562
- const callArgs = mockScriptExecutionRepo.appendExecutionLog.mock.calls[0];
561
+ expect(mockAdminProcessRepo.appendProcessLog).toHaveBeenCalled();
562
+ const callArgs = mockAdminProcessRepo.appendProcessLog.mock.calls[0];
563
563
  expect(callArgs[0]).toBe('exec_123');
564
564
  expect(callArgs[1].level).toBe('warn');
565
565
  expect(callArgs[1].message).toBe('Warning message');
@@ -572,14 +572,14 @@ describe('AdminFriggCommands', () => {
572
572
 
573
573
  await new Promise(resolve => setImmediate(resolve));
574
574
 
575
- expect(mockScriptExecutionRepo.appendExecutionLog).not.toHaveBeenCalled();
575
+ expect(mockAdminProcessRepo.appendProcessLog).not.toHaveBeenCalled();
576
576
  });
577
577
 
578
578
  it('log() handles persistence failure gracefully', async () => {
579
579
  const commands = new AdminFriggCommands({ executionId: 'exec_123' });
580
580
  // Force repository creation
581
- commands.scriptExecutionRepository;
582
- mockScriptExecutionRepo.appendExecutionLog.mockRejectedValue(new Error('DB Error'));
581
+ commands.adminProcessRepository;
582
+ mockAdminProcessRepo.appendProcessLog.mockRejectedValue(new Error('DB Error'));
583
583
 
584
584
  // Should not throw
585
585
  expect(() => commands.log('error', 'Test error')).not.toThrow();
@@ -36,9 +36,9 @@ describe('ScriptRunner', () => {
36
36
  scriptFactory = new ScriptFactory([TestScript]);
37
37
 
38
38
  mockCommands = {
39
- createScriptExecution: jest.fn(),
40
- updateScriptExecutionStatus: jest.fn(),
41
- completeScriptExecution: jest.fn(),
39
+ createAdminProcess: jest.fn(),
40
+ updateAdminProcessState: jest.fn(),
41
+ completeAdminProcess: jest.fn(),
42
42
  };
43
43
 
44
44
  mockFrigg = {
@@ -49,11 +49,11 @@ describe('ScriptRunner', () => {
49
49
  createAdminScriptCommands.mockReturnValue(mockCommands);
50
50
  createAdminFriggCommands.mockReturnValue(mockFrigg);
51
51
 
52
- mockCommands.createScriptExecution.mockResolvedValue({
52
+ mockCommands.createAdminProcess.mockResolvedValue({
53
53
  id: 'exec-123',
54
54
  });
55
- mockCommands.updateScriptExecutionStatus.mockResolvedValue({});
56
- mockCommands.completeScriptExecution.mockResolvedValue({ success: true });
55
+ mockCommands.updateAdminProcessState.mockResolvedValue({});
56
+ mockCommands.completeAdminProcess.mockResolvedValue({ success: true });
57
57
  });
58
58
 
59
59
  afterEach(() => {
@@ -76,7 +76,7 @@ describe('ScriptRunner', () => {
76
76
  expect(result.executionId).toBe('exec-123');
77
77
  expect(result.metrics.durationMs).toBeGreaterThanOrEqual(0);
78
78
 
79
- expect(mockCommands.createScriptExecution).toHaveBeenCalledWith({
79
+ expect(mockCommands.createAdminProcess).toHaveBeenCalledWith({
80
80
  scriptName: 'test-script',
81
81
  scriptVersion: '1.0.0',
82
82
  trigger: 'MANUAL',
@@ -85,15 +85,15 @@ describe('ScriptRunner', () => {
85
85
  audit: { apiKeyName: 'test-key' },
86
86
  });
87
87
 
88
- expect(mockCommands.updateScriptExecutionStatus).toHaveBeenCalledWith(
88
+ expect(mockCommands.updateAdminProcessState).toHaveBeenCalledWith(
89
89
  'exec-123',
90
90
  'RUNNING'
91
91
  );
92
92
 
93
- expect(mockCommands.completeScriptExecution).toHaveBeenCalledWith(
93
+ expect(mockCommands.completeAdminProcess).toHaveBeenCalledWith(
94
94
  'exec-123',
95
95
  expect.objectContaining({
96
- status: 'COMPLETED',
96
+ state: 'COMPLETED',
97
97
  output: { success: true, params: { foo: 'bar' } },
98
98
  metrics: expect.objectContaining({
99
99
  durationMs: expect.any(Number),
@@ -128,10 +128,10 @@ describe('ScriptRunner', () => {
128
128
  expect(result.scriptName).toBe('failing-script');
129
129
  expect(result.error.message).toBe('Script failed');
130
130
 
131
- expect(mockCommands.completeScriptExecution).toHaveBeenCalledWith(
131
+ expect(mockCommands.completeAdminProcess).toHaveBeenCalledWith(
132
132
  'exec-123',
133
133
  expect.objectContaining({
134
- status: 'FAILED',
134
+ state: 'FAILED',
135
135
  error: expect.objectContaining({
136
136
  message: 'Script failed',
137
137
  }),
@@ -178,14 +178,144 @@ describe('ScriptRunner', () => {
178
178
  });
179
179
 
180
180
  expect(result.executionId).toBe('existing-exec-456');
181
- expect(mockCommands.createScriptExecution).not.toHaveBeenCalled();
182
- expect(mockCommands.updateScriptExecutionStatus).toHaveBeenCalledWith(
181
+ expect(mockCommands.createAdminProcess).not.toHaveBeenCalled();
182
+ expect(mockCommands.updateAdminProcessState).toHaveBeenCalledWith(
183
183
  'existing-exec-456',
184
184
  'RUNNING'
185
185
  );
186
186
  });
187
187
  });
188
188
 
189
+ describe('dry-run mode', () => {
190
+ it('should return preview without executing script', async () => {
191
+ const runner = new ScriptRunner({ scriptFactory, commands: mockCommands });
192
+
193
+ const result = await runner.execute('test-script', { foo: 'bar' }, {
194
+ trigger: 'MANUAL',
195
+ dryRun: true,
196
+ });
197
+
198
+ expect(result.dryRun).toBe(true);
199
+ expect(result.status).toBe('DRY_RUN_VALID');
200
+ expect(result.scriptName).toBe('test-script');
201
+ expect(result.preview.script.name).toBe('test-script');
202
+ expect(result.preview.script.version).toBe('1.0.0');
203
+ expect(result.preview.input).toEqual({ foo: 'bar' });
204
+ expect(result.message).toContain('validation passed');
205
+
206
+ // Should NOT create execution record or call commands
207
+ expect(mockCommands.createAdminProcess).not.toHaveBeenCalled();
208
+ expect(mockCommands.updateAdminProcessState).not.toHaveBeenCalled();
209
+ expect(mockCommands.completeAdminProcess).not.toHaveBeenCalled();
210
+ });
211
+
212
+ it('should validate required parameters in dry-run', async () => {
213
+ class SchemaScript extends AdminScriptBase {
214
+ static Definition = {
215
+ name: 'schema-script',
216
+ version: '1.0.0',
217
+ description: 'Script with schema',
218
+ inputSchema: {
219
+ type: 'object',
220
+ required: ['requiredParam'],
221
+ properties: {
222
+ requiredParam: { type: 'string' },
223
+ optionalParam: { type: 'number' },
224
+ },
225
+ },
226
+ };
227
+
228
+ async execute() {
229
+ return {};
230
+ }
231
+ }
232
+
233
+ scriptFactory.register(SchemaScript);
234
+ const runner = new ScriptRunner({ scriptFactory, commands: mockCommands });
235
+
236
+ // Missing required parameter
237
+ const result = await runner.execute('schema-script', {}, {
238
+ dryRun: true,
239
+ });
240
+
241
+ expect(result.status).toBe('DRY_RUN_INVALID');
242
+ expect(result.preview.validation.valid).toBe(false);
243
+ expect(result.preview.validation.errors).toContain('Missing required parameter: requiredParam');
244
+ });
245
+
246
+ it('should validate parameter types in dry-run', async () => {
247
+ class TypedScript extends AdminScriptBase {
248
+ static Definition = {
249
+ name: 'typed-script',
250
+ version: '1.0.0',
251
+ description: 'Script with typed params',
252
+ inputSchema: {
253
+ type: 'object',
254
+ properties: {
255
+ count: { type: 'integer' },
256
+ name: { type: 'string' },
257
+ enabled: { type: 'boolean' },
258
+ },
259
+ },
260
+ };
261
+
262
+ async execute() {
263
+ return {};
264
+ }
265
+ }
266
+
267
+ scriptFactory.register(TypedScript);
268
+ const runner = new ScriptRunner({ scriptFactory, commands: mockCommands });
269
+
270
+ const result = await runner.execute('typed-script', {
271
+ count: 'not-a-number',
272
+ name: 123,
273
+ enabled: 'true',
274
+ }, {
275
+ dryRun: true,
276
+ });
277
+
278
+ expect(result.status).toBe('DRY_RUN_INVALID');
279
+ expect(result.preview.validation.errors).toHaveLength(3);
280
+ });
281
+
282
+ it('should pass validation with correct parameters', async () => {
283
+ class ValidScript extends AdminScriptBase {
284
+ static Definition = {
285
+ name: 'valid-script',
286
+ version: '1.0.0',
287
+ description: 'Script for validation',
288
+ inputSchema: {
289
+ type: 'object',
290
+ required: ['name'],
291
+ properties: {
292
+ name: { type: 'string' },
293
+ count: { type: 'integer' },
294
+ },
295
+ },
296
+ };
297
+
298
+ async execute() {
299
+ return {};
300
+ }
301
+ }
302
+
303
+ scriptFactory.register(ValidScript);
304
+ const runner = new ScriptRunner({ scriptFactory, commands: mockCommands });
305
+
306
+ const result = await runner.execute('valid-script', {
307
+ name: 'test',
308
+ count: 42,
309
+ }, {
310
+ dryRun: true,
311
+ });
312
+
313
+ expect(result.status).toBe('DRY_RUN_VALID');
314
+ expect(result.preview.validation.valid).toBe(true);
315
+ expect(result.preview.validation.errors).toHaveLength(0);
316
+ });
317
+ });
318
+
189
319
  describe('createScriptRunner()', () => {
190
320
  it('should create runner with default factory', () => {
191
321
  const runner = createScriptRunner();
@@ -25,7 +25,7 @@ class AdminFriggCommands {
25
25
  this._userRepository = null;
26
26
  this._moduleRepository = null;
27
27
  this._credentialRepository = null;
28
- this._scriptExecutionRepository = null;
28
+ this._adminProcessRepository = null;
29
29
  }
30
30
 
31
31
  // ==================== LAZY-LOADED REPOSITORIES ====================
@@ -62,12 +62,12 @@ class AdminFriggCommands {
62
62
  return this._credentialRepository;
63
63
  }
64
64
 
65
- get scriptExecutionRepository() {
66
- if (!this._scriptExecutionRepository) {
67
- const { createScriptExecutionRepository } = require('@friggframework/core/admin-scripts/repositories/script-execution-repository-factory');
68
- this._scriptExecutionRepository = createScriptExecutionRepository();
65
+ get adminProcessRepository() {
66
+ if (!this._adminProcessRepository) {
67
+ const { createAdminProcessRepository } = require('@friggframework/core/admin-scripts/repositories/admin-process-repository-factory');
68
+ this._adminProcessRepository = createAdminProcessRepository();
69
69
  }
70
- return this._scriptExecutionRepository;
70
+ return this._adminProcessRepository;
71
71
  }
72
72
 
73
73
  // ==================== INTEGRATION QUERIES ====================
@@ -209,7 +209,7 @@ class AdminFriggCommands {
209
209
 
210
210
  // Persist to execution record if we have an executionId
211
211
  if (this.executionId) {
212
- this.scriptExecutionRepository.appendExecutionLog(this.executionId, entry)
212
+ this.adminProcessRepository.appendProcessLog(this.executionId, entry)
213
213
  .catch(err => console.error('Failed to persist log:', err));
214
214
  }
215
215
 
@@ -1,5 +1,4 @@
1
- const { createScriptExecutionRepository } = require('@friggframework/core/admin-scripts/repositories/script-execution-repository-factory');
2
- const { createAdminApiKeyRepository } = require('@friggframework/core/admin-scripts/repositories/admin-api-key-repository-factory');
1
+ const { createAdminProcessRepository } = require('@friggframework/core/admin-scripts/repositories/admin-process-repository-factory');
3
2
 
4
3
  /**
5
4
  * Admin Script Base Class
@@ -87,8 +86,7 @@ class AdminScriptBase {
87
86
  this.integrationFactory = params.integrationFactory || null;
88
87
 
89
88
  // OPTIONAL: Injected repositories (for testing or custom implementations)
90
- this.scriptExecutionRepository = params.scriptExecutionRepository || null;
91
- this.adminApiKeyRepository = params.adminApiKeyRepository || null;
89
+ this.adminProcessRepository = params.adminProcessRepository || null;
92
90
  }
93
91
 
94
92
  /**