@friggframework/admin-scripts 2.0.0--canary.522.cbd3d5a.0 → 2.0.0--canary.517.21b69ac.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 (31) hide show
  1. package/PR_517_REVIEW_TRACKER.md +56 -0
  2. package/index.js +12 -3
  3. package/package.json +6 -9
  4. package/src/application/__tests__/admin-frigg-commands.test.js +19 -19
  5. package/src/application/__tests__/admin-script-base.test.js +100 -84
  6. package/src/application/__tests__/script-runner.test.js +146 -16
  7. package/src/application/admin-frigg-commands.js +20 -32
  8. package/src/application/admin-script-base.js +20 -99
  9. package/src/application/script-runner.js +131 -135
  10. package/src/application/use-cases/__tests__/delete-schedule-use-case.test.js +168 -0
  11. package/src/application/use-cases/__tests__/get-effective-schedule-use-case.test.js +114 -0
  12. package/src/application/use-cases/__tests__/upsert-schedule-use-case.test.js +201 -0
  13. package/src/application/use-cases/delete-schedule-use-case.js +108 -0
  14. package/src/application/use-cases/get-effective-schedule-use-case.js +78 -0
  15. package/src/application/use-cases/index.js +18 -0
  16. package/src/application/use-cases/upsert-schedule-use-case.js +127 -0
  17. package/src/builtins/__tests__/integration-health-check.test.js +67 -60
  18. package/src/builtins/__tests__/oauth-token-refresh.test.js +45 -37
  19. package/src/builtins/integration-health-check.js +23 -24
  20. package/src/builtins/oauth-token-refresh.js +19 -20
  21. package/src/infrastructure/__tests__/admin-auth-middleware.test.js +32 -95
  22. package/src/infrastructure/__tests__/admin-script-router.test.js +46 -47
  23. package/src/infrastructure/admin-auth-middleware.js +5 -43
  24. package/src/infrastructure/admin-script-router.js +38 -32
  25. package/src/infrastructure/script-executor-handler.js +2 -2
  26. package/src/application/__tests__/dry-run-http-interceptor.test.js +0 -313
  27. package/src/application/__tests__/dry-run-repository-wrapper.test.js +0 -257
  28. package/src/application/__tests__/schedule-management-use-case.test.js +0 -276
  29. package/src/application/dry-run-http-interceptor.js +0 -296
  30. package/src/application/dry-run-repository-wrapper.js +0 -261
  31. package/src/application/schedule-management-use-case.js +0 -230
@@ -4,13 +4,8 @@ const { AdminScriptBase } = require('../../application/admin-script-base');
4
4
 
5
5
  // Mock dependencies
6
6
  jest.mock('../admin-auth-middleware', () => ({
7
- adminAuthMiddleware: (req, res, next) => {
8
- // Mock auth - attach admin audit info
9
- req.adminAudit = {
10
- apiKeyName: 'test-key',
11
- apiKeyLast4: '1234',
12
- ipAddress: '127.0.0.1',
13
- };
7
+ validateAdminApiKey: (req, res, next) => {
8
+ // Mock auth - no audit trail with simplified auth
14
9
  next();
15
10
  },
16
11
  }));
@@ -59,8 +54,8 @@ describe('Admin Script Router', () => {
59
54
  };
60
55
 
61
56
  mockCommands = {
62
- createScriptExecution: jest.fn(),
63
- findScriptExecutionById: jest.fn(),
57
+ createAdminProcess: jest.fn(),
58
+ findAdminProcessById: jest.fn(),
64
59
  findRecentExecutions: jest.fn(),
65
60
  };
66
61
 
@@ -104,7 +99,7 @@ describe('Admin Script Router', () => {
104
99
  version: '1.0.0',
105
100
  description: 'Test script',
106
101
  category: 'test',
107
- requiresIntegrationFactory: false,
102
+ requireIntegrationInstance: false,
108
103
  schedule: null,
109
104
  });
110
105
  });
@@ -143,7 +138,7 @@ describe('Admin Script Router', () => {
143
138
  });
144
139
  });
145
140
 
146
- describe('POST /admin/scripts/:scriptName/execute', () => {
141
+ describe('POST /admin/scripts/:scriptName', () => {
147
142
  it('should execute script synchronously', async () => {
148
143
  mockRunner.execute.mockResolvedValue({
149
144
  executionId: 'exec-123',
@@ -154,7 +149,7 @@ describe('Admin Script Router', () => {
154
149
  });
155
150
 
156
151
  const response = await request(app)
157
- .post('/admin/scripts/test-script/execute')
152
+ .post('/admin/scripts/test-script')
158
153
  .send({
159
154
  params: { foo: 'bar' },
160
155
  mode: 'sync',
@@ -174,12 +169,12 @@ describe('Admin Script Router', () => {
174
169
  });
175
170
 
176
171
  it('should queue script for async execution', async () => {
177
- mockCommands.createScriptExecution.mockResolvedValue({
172
+ mockCommands.createAdminProcess.mockResolvedValue({
178
173
  id: 'exec-456',
179
174
  });
180
175
 
181
176
  const response = await request(app)
182
- .post('/admin/scripts/test-script/execute')
177
+ .post('/admin/scripts/test-script')
183
178
  .send({
184
179
  params: { foo: 'bar' },
185
180
  mode: 'async',
@@ -198,12 +193,12 @@ describe('Admin Script Router', () => {
198
193
  });
199
194
 
200
195
  it('should default to async mode', async () => {
201
- mockCommands.createScriptExecution.mockResolvedValue({
196
+ mockCommands.createAdminProcess.mockResolvedValue({
202
197
  id: 'exec-789',
203
198
  });
204
199
 
205
200
  const response = await request(app)
206
- .post('/admin/scripts/test-script/execute')
201
+ .post('/admin/scripts/test-script')
207
202
  .send({
208
203
  params: { foo: 'bar' },
209
204
  });
@@ -216,7 +211,7 @@ describe('Admin Script Router', () => {
216
211
  mockFactory.has.mockReturnValue(false);
217
212
 
218
213
  const response = await request(app)
219
- .post('/admin/scripts/non-existent/execute')
214
+ .post('/admin/scripts/non-existent')
220
215
  .send({
221
216
  params: {},
222
217
  });
@@ -226,15 +221,15 @@ describe('Admin Script Router', () => {
226
221
  });
227
222
  });
228
223
 
229
- describe('GET /admin/executions/:executionId', () => {
224
+ describe('GET /admin/scripts/:scriptName/executions/:executionId', () => {
230
225
  it('should return execution details', async () => {
231
- mockCommands.findScriptExecutionById.mockResolvedValue({
226
+ mockCommands.findAdminProcessById.mockResolvedValue({
232
227
  id: 'exec-123',
233
228
  scriptName: 'test-script',
234
229
  status: 'COMPLETED',
235
230
  });
236
231
 
237
- const response = await request(app).get('/admin/executions/exec-123');
232
+ const response = await request(app).get('/admin/scripts/test-script/executions/exec-123');
238
233
 
239
234
  expect(response.status).toBe(200);
240
235
  expect(response.body.id).toBe('exec-123');
@@ -242,14 +237,14 @@ describe('Admin Script Router', () => {
242
237
  });
243
238
 
244
239
  it('should return 404 for non-existent execution', async () => {
245
- mockCommands.findScriptExecutionById.mockResolvedValue({
240
+ mockCommands.findAdminProcessById.mockResolvedValue({
246
241
  error: 404,
247
242
  reason: 'Execution not found',
248
243
  code: 'EXECUTION_NOT_FOUND',
249
244
  });
250
245
 
251
246
  const response = await request(app).get(
252
- '/admin/executions/non-existent'
247
+ '/admin/scripts/test-script/executions/non-existent'
253
248
  );
254
249
 
255
250
  expect(response.status).toBe(404);
@@ -257,24 +252,28 @@ describe('Admin Script Router', () => {
257
252
  });
258
253
  });
259
254
 
260
- describe('GET /admin/executions', () => {
261
- it('should list recent executions', async () => {
255
+ describe('GET /admin/scripts/:scriptName/executions', () => {
256
+ it('should list executions for specific script', async () => {
262
257
  mockCommands.findRecentExecutions.mockResolvedValue([
263
258
  { id: 'exec-1', scriptName: 'test-script', status: 'COMPLETED' },
264
259
  { id: 'exec-2', scriptName: 'test-script', status: 'RUNNING' },
265
260
  ]);
266
261
 
267
- const response = await request(app).get('/admin/executions');
262
+ const response = await request(app).get('/admin/scripts/test-script/executions');
268
263
 
269
264
  expect(response.status).toBe(200);
270
265
  expect(response.body.executions).toHaveLength(2);
266
+ expect(mockCommands.findRecentExecutions).toHaveBeenCalledWith({
267
+ scriptName: 'test-script',
268
+ limit: 50,
269
+ });
271
270
  });
272
271
 
273
272
  it('should accept query parameters', async () => {
274
273
  mockCommands.findRecentExecutions.mockResolvedValue([]);
275
274
 
276
275
  await request(app).get(
277
- '/admin/executions?scriptName=test-script&status=COMPLETED&limit=10'
276
+ '/admin/scripts/test-script/executions?status=COMPLETED&limit=10'
278
277
  );
279
278
 
280
279
  expect(mockCommands.findRecentExecutions).toHaveBeenCalledWith({
@@ -294,8 +293,8 @@ describe('Admin Script Router', () => {
294
293
  timezone: 'America/New_York',
295
294
  lastTriggeredAt: new Date('2025-01-01T09:00:00Z'),
296
295
  nextTriggerAt: new Date('2025-01-02T09:00:00Z'),
297
- awsScheduleArn: 'arn:aws:events:us-east-1:123456789012:rule/test',
298
- awsScheduleName: 'test-script-schedule',
296
+ externalScheduleId: 'arn:aws:events:us-east-1:123456789012:rule/test',
297
+ externalScheduleName: 'test-script-schedule',
299
298
  createdAt: new Date('2025-01-01T00:00:00Z'),
300
299
  updatedAt: new Date('2025-01-01T00:00:00Z'),
301
300
  };
@@ -471,7 +470,7 @@ describe('Admin Script Router', () => {
471
470
  };
472
471
 
473
472
  mockCommands.upsertSchedule = jest.fn().mockResolvedValue(newSchedule);
474
- mockCommands.updateScheduleAwsInfo = jest.fn().mockResolvedValue(newSchedule);
473
+ mockCommands.updateScheduleExternalInfo = jest.fn().mockResolvedValue(newSchedule);
475
474
  mockSchedulerAdapter.createSchedule.mockResolvedValue({
476
475
  scheduleArn: 'arn:aws:scheduler:us-east-1:123456789012:schedule/frigg-admin-scripts/frigg-script-test-script',
477
476
  scheduleName: 'frigg-script-test-script',
@@ -491,11 +490,11 @@ describe('Admin Script Router', () => {
491
490
  cronExpression: '0 12 * * *',
492
491
  timezone: 'America/Los_Angeles',
493
492
  });
494
- expect(mockCommands.updateScheduleAwsInfo).toHaveBeenCalledWith('test-script', {
495
- awsScheduleArn: 'arn:aws:scheduler:us-east-1:123456789012:schedule/frigg-admin-scripts/frigg-script-test-script',
496
- awsScheduleName: 'frigg-script-test-script',
493
+ expect(mockCommands.updateScheduleExternalInfo).toHaveBeenCalledWith('test-script', {
494
+ externalScheduleId: 'arn:aws:scheduler:us-east-1:123456789012:schedule/frigg-admin-scripts/frigg-script-test-script',
495
+ externalScheduleName: 'frigg-script-test-script',
497
496
  });
498
- expect(response.body.schedule.awsScheduleArn).toBe('arn:aws:scheduler:us-east-1:123456789012:schedule/frigg-admin-scripts/frigg-script-test-script');
497
+ expect(response.body.schedule.externalScheduleId).toBe('arn:aws:scheduler:us-east-1:123456789012:schedule/frigg-admin-scripts/frigg-script-test-script');
499
498
  });
500
499
 
501
500
  it('should delete EventBridge schedule when disabling existing schedule', async () => {
@@ -504,14 +503,14 @@ describe('Admin Script Router', () => {
504
503
  enabled: false,
505
504
  cronExpression: null,
506
505
  timezone: 'UTC',
507
- awsScheduleArn: 'arn:aws:scheduler:us-east-1:123456789012:schedule/frigg-admin-scripts/frigg-script-test-script',
508
- awsScheduleName: 'frigg-script-test-script',
506
+ externalScheduleId: 'arn:aws:scheduler:us-east-1:123456789012:schedule/frigg-admin-scripts/frigg-script-test-script',
507
+ externalScheduleName: 'frigg-script-test-script',
509
508
  createdAt: new Date(),
510
509
  updatedAt: new Date(),
511
510
  };
512
511
 
513
512
  mockCommands.upsertSchedule = jest.fn().mockResolvedValue(existingSchedule);
514
- mockCommands.updateScheduleAwsInfo = jest.fn().mockResolvedValue(existingSchedule);
513
+ mockCommands.updateScheduleExternalInfo = jest.fn().mockResolvedValue(existingSchedule);
515
514
  mockSchedulerAdapter.deleteSchedule.mockResolvedValue();
516
515
 
517
516
  const response = await request(app)
@@ -522,9 +521,9 @@ describe('Admin Script Router', () => {
522
521
 
523
522
  expect(response.status).toBe(200);
524
523
  expect(mockSchedulerAdapter.deleteSchedule).toHaveBeenCalledWith('test-script');
525
- expect(mockCommands.updateScheduleAwsInfo).toHaveBeenCalledWith('test-script', {
526
- awsScheduleArn: null,
527
- awsScheduleName: null,
524
+ expect(mockCommands.updateScheduleExternalInfo).toHaveBeenCalledWith('test-script', {
525
+ externalScheduleId: null,
526
+ externalScheduleName: null,
528
527
  });
529
528
  });
530
529
 
@@ -633,7 +632,7 @@ describe('Admin Script Router', () => {
633
632
  expect(response.body.code).toBe('SCRIPT_NOT_FOUND');
634
633
  });
635
634
 
636
- it('should delete EventBridge schedule when AWS rule exists', async () => {
635
+ it('should delete EventBridge schedule when external rule exists', async () => {
637
636
  mockCommands.deleteSchedule = jest.fn().mockResolvedValue({
638
637
  acknowledged: true,
639
638
  deletedCount: 1,
@@ -641,8 +640,8 @@ describe('Admin Script Router', () => {
641
640
  scriptName: 'test-script',
642
641
  enabled: true,
643
642
  cronExpression: '0 12 * * *',
644
- awsScheduleArn: 'arn:aws:scheduler:us-east-1:123456789012:schedule/frigg-admin-scripts/frigg-script-test-script',
645
- awsScheduleName: 'frigg-script-test-script',
643
+ externalScheduleId: 'arn:aws:scheduler:us-east-1:123456789012:schedule/frigg-admin-scripts/frigg-script-test-script',
644
+ externalScheduleName: 'frigg-script-test-script',
646
645
  },
647
646
  });
648
647
  mockSchedulerAdapter.deleteSchedule.mockResolvedValue();
@@ -655,7 +654,7 @@ describe('Admin Script Router', () => {
655
654
  expect(mockSchedulerAdapter.deleteSchedule).toHaveBeenCalledWith('test-script');
656
655
  });
657
656
 
658
- it('should not call scheduler when no AWS rule exists', async () => {
657
+ it('should not call scheduler when no external rule exists', async () => {
659
658
  mockCommands.deleteSchedule = jest.fn().mockResolvedValue({
660
659
  acknowledged: true,
661
660
  deletedCount: 1,
@@ -663,7 +662,7 @@ describe('Admin Script Router', () => {
663
662
  scriptName: 'test-script',
664
663
  enabled: true,
665
664
  cronExpression: '0 12 * * *',
666
- // No awsScheduleArn
665
+ // No externalScheduleId
667
666
  },
668
667
  });
669
668
 
@@ -683,10 +682,10 @@ describe('Admin Script Router', () => {
683
682
  scriptName: 'test-script',
684
683
  enabled: true,
685
684
  cronExpression: '0 12 * * *',
686
- awsScheduleArn: 'arn:aws:scheduler:us-east-1:123456789012:schedule/frigg-admin-scripts/frigg-script-test-script',
685
+ externalScheduleId: 'arn:aws:scheduler:us-east-1:123456789012:schedule/frigg-admin-scripts/frigg-script-test-script',
687
686
  },
688
687
  });
689
- mockSchedulerAdapter.deleteSchedule.mockRejectedValue(new Error('AWS Scheduler delete failed'));
688
+ mockSchedulerAdapter.deleteSchedule.mockRejectedValue(new Error('Scheduler delete failed'));
690
689
 
691
690
  const response = await request(app).delete(
692
691
  '/admin/scripts/test-script/schedule'
@@ -695,7 +694,7 @@ describe('Admin Script Router', () => {
695
694
  // Request should succeed despite scheduler error
696
695
  expect(response.status).toBe(200);
697
696
  expect(response.body.success).toBe(true);
698
- expect(response.body.schedulerWarning).toBe('AWS Scheduler delete failed');
697
+ expect(response.body.schedulerWarning).toBe('Scheduler delete failed');
699
698
  });
700
699
  });
701
700
  });
@@ -1,49 +1,11 @@
1
- const { createAdminScriptCommands } = require('@friggframework/core/application/commands/admin-script-commands');
2
-
3
1
  /**
4
2
  * Admin API Key Authentication Middleware
5
3
  *
6
- * Validates admin API keys for script endpoints.
7
- * Expects: Authorization: Bearer <api-key>
4
+ * Re-exports shared admin auth middleware from @friggframework/core.
5
+ * Uses simple ENV-based API key validation.
6
+ * Expects: x-frigg-admin-api-key header
8
7
  */
9
- async function adminAuthMiddleware(req, res, next) {
10
- try {
11
- const authHeader = req.headers.authorization;
12
-
13
- if (!authHeader || !authHeader.startsWith('Bearer ')) {
14
- return res.status(401).json({
15
- error: 'Missing or invalid Authorization header',
16
- code: 'MISSING_AUTH'
17
- });
18
- }
19
-
20
- const apiKey = authHeader.substring(7); // Remove 'Bearer '
21
- const commands = createAdminScriptCommands();
22
- const result = await commands.validateAdminApiKey(apiKey);
23
-
24
- if (result.error) {
25
- return res.status(result.error).json({
26
- error: result.reason,
27
- code: result.code
28
- });
29
- }
30
-
31
- // Attach validated key info to request for audit trail
32
- req.adminApiKey = result.apiKey;
33
- req.adminAudit = {
34
- apiKeyName: result.apiKey.name,
35
- apiKeyLast4: result.apiKey.keyLast4,
36
- ipAddress: req.ip || req.connection?.remoteAddress || 'unknown'
37
- };
38
8
 
39
- next();
40
- } catch (error) {
41
- console.error('Admin auth middleware error:', error);
42
- res.status(500).json({
43
- error: 'Authentication failed',
44
- code: 'AUTH_ERROR'
45
- });
46
- }
47
- }
9
+ const { validateAdminApiKey } = require('@friggframework/core/handlers/middleware/admin-auth');
48
10
 
49
- module.exports = { adminAuthMiddleware };
11
+ module.exports = { validateAdminApiKey };
@@ -1,28 +1,36 @@
1
1
  const express = require('express');
2
2
  const serverless = require('serverless-http');
3
- const { adminAuthMiddleware } = require('./admin-auth-middleware');
3
+ const { validateAdminApiKey } = require('./admin-auth-middleware');
4
4
  const { getScriptFactory } = require('../application/script-factory');
5
5
  const { createScriptRunner } = require('../application/script-runner');
6
6
  const { createAdminScriptCommands } = require('@friggframework/core/application/commands/admin-script-commands');
7
7
  const { QueuerUtil } = require('@friggframework/core/queues');
8
8
  const { createSchedulerAdapter } = require('../adapters/scheduler-adapter-factory');
9
- const { ScheduleManagementUseCase } = require('../application/schedule-management-use-case');
9
+ const {
10
+ GetEffectiveScheduleUseCase,
11
+ UpsertScheduleUseCase,
12
+ DeleteScheduleUseCase,
13
+ } = require('../application/use-cases');
10
14
 
11
15
  const router = express.Router();
12
16
 
13
17
  // Apply auth middleware to all admin routes
14
- router.use(adminAuthMiddleware);
18
+ router.use(validateAdminApiKey);
15
19
 
16
20
  /**
17
- * Create ScheduleManagementUseCase instance
21
+ * Create schedule use case instances
18
22
  * @private
19
23
  */
20
- function createScheduleManagementUseCase() {
21
- return new ScheduleManagementUseCase({
22
- commands: createAdminScriptCommands(),
23
- schedulerAdapter: createSchedulerAdapter(),
24
- scriptFactory: getScriptFactory(),
25
- });
24
+ function createScheduleUseCases() {
25
+ const commands = createAdminScriptCommands();
26
+ const schedulerAdapter = createSchedulerAdapter();
27
+ const scriptFactory = getScriptFactory();
28
+
29
+ return {
30
+ getEffectiveSchedule: new GetEffectiveScheduleUseCase({ commands, scriptFactory }),
31
+ upsertSchedule: new UpsertScheduleUseCase({ commands, schedulerAdapter, scriptFactory }),
32
+ deleteSchedule: new DeleteScheduleUseCase({ commands, schedulerAdapter, scriptFactory }),
33
+ };
26
34
  }
27
35
 
28
36
  /**
@@ -40,8 +48,8 @@ router.get('/scripts', async (req, res) => {
40
48
  version: s.definition.version,
41
49
  description: s.definition.description,
42
50
  category: s.definition.display?.category || 'custom',
43
- requiresIntegrationFactory:
44
- s.definition.config?.requiresIntegrationFactory || false,
51
+ requireIntegrationInstance:
52
+ s.definition.config?.requireIntegrationInstance || false,
45
53
  schedule: s.definition.schedule || null,
46
54
  })),
47
55
  });
@@ -87,10 +95,10 @@ router.get('/scripts/:scriptName', async (req, res) => {
87
95
  });
88
96
 
89
97
  /**
90
- * POST /admin/scripts/:scriptName/execute
98
+ * POST /admin/scripts/:scriptName
91
99
  * Execute a script (sync, async, or dry-run)
92
100
  */
93
- router.post('/scripts/:scriptName/execute', async (req, res) => {
101
+ router.post('/scripts/:scriptName', async (req, res) => {
94
102
  try {
95
103
  const { scriptName } = req.params;
96
104
  const { params = {}, mode = 'async', dryRun = false } = req.body;
@@ -110,7 +118,6 @@ router.post('/scripts/:scriptName/execute', async (req, res) => {
110
118
  trigger: 'MANUAL',
111
119
  mode: 'sync',
112
120
  dryRun: true,
113
- audit: req.adminAudit,
114
121
  });
115
122
  return res.json(result);
116
123
  }
@@ -121,20 +128,18 @@ router.post('/scripts/:scriptName/execute', async (req, res) => {
121
128
  const result = await runner.execute(scriptName, params, {
122
129
  trigger: 'MANUAL',
123
130
  mode: 'sync',
124
- audit: req.adminAudit,
125
131
  });
126
132
  return res.json(result);
127
133
  }
128
134
 
129
135
  // Async execution - queue and return immediately
130
136
  const commands = createAdminScriptCommands();
131
- const execution = await commands.createScriptExecution({
137
+ const execution = await commands.createAdminProcess({
132
138
  scriptName,
133
139
  scriptVersion: factory.get(scriptName).Definition.version,
134
140
  trigger: 'MANUAL',
135
141
  mode: 'async',
136
142
  input: params,
137
- audit: req.adminAudit,
138
143
  });
139
144
 
140
145
  // Queue the execution
@@ -161,14 +166,14 @@ router.post('/scripts/:scriptName/execute', async (req, res) => {
161
166
  });
162
167
 
163
168
  /**
164
- * GET /admin/executions/:executionId
165
- * Get execution status
169
+ * GET /admin/scripts/:scriptName/executions/:executionId
170
+ * Get execution status for specific script
166
171
  */
167
- router.get('/executions/:executionId', async (req, res) => {
172
+ router.get('/scripts/:scriptName/executions/:executionId', async (req, res) => {
168
173
  try {
169
174
  const { executionId } = req.params;
170
175
  const commands = createAdminScriptCommands();
171
- const execution = await commands.findScriptExecutionById(executionId);
176
+ const execution = await commands.findAdminProcessById(executionId);
172
177
 
173
178
  if (execution.error) {
174
179
  return res.status(execution.error).json({
@@ -185,12 +190,13 @@ router.get('/executions/:executionId', async (req, res) => {
185
190
  });
186
191
 
187
192
  /**
188
- * GET /admin/executions
189
- * List recent executions
193
+ * GET /admin/scripts/:scriptName/executions
194
+ * List recent executions for specific script
190
195
  */
191
- router.get('/executions', async (req, res) => {
196
+ router.get('/scripts/:scriptName/executions', async (req, res) => {
192
197
  try {
193
- const { scriptName, status, limit = 50 } = req.query;
198
+ const { scriptName } = req.params;
199
+ const { status, limit = 50 } = req.query;
194
200
  const commands = createAdminScriptCommands();
195
201
 
196
202
  const executions = await commands.findRecentExecutions({
@@ -213,9 +219,9 @@ router.get('/executions', async (req, res) => {
213
219
  router.get('/scripts/:scriptName/schedule', async (req, res) => {
214
220
  try {
215
221
  const { scriptName } = req.params;
216
- const useCase = createScheduleManagementUseCase();
222
+ const { getEffectiveSchedule } = createScheduleUseCases();
217
223
 
218
- const result = await useCase.getEffectiveSchedule(scriptName);
224
+ const result = await getEffectiveSchedule.execute(scriptName);
219
225
 
220
226
  res.json({
221
227
  source: result.source,
@@ -242,9 +248,9 @@ router.put('/scripts/:scriptName/schedule', async (req, res) => {
242
248
  try {
243
249
  const { scriptName } = req.params;
244
250
  const { enabled, cronExpression, timezone } = req.body;
245
- const useCase = createScheduleManagementUseCase();
251
+ const { upsertSchedule } = createScheduleUseCases();
246
252
 
247
- const result = await useCase.upsertSchedule(scriptName, {
253
+ const result = await upsertSchedule.execute(scriptName, {
248
254
  enabled,
249
255
  cronExpression,
250
256
  timezone,
@@ -283,9 +289,9 @@ router.put('/scripts/:scriptName/schedule', async (req, res) => {
283
289
  router.delete('/scripts/:scriptName/schedule', async (req, res) => {
284
290
  try {
285
291
  const { scriptName } = req.params;
286
- const useCase = createScheduleManagementUseCase();
292
+ const { deleteSchedule } = createScheduleUseCases();
287
293
 
288
- const result = await useCase.deleteSchedule(scriptName);
294
+ const result = await deleteSchedule.execute(scriptName);
289
295
 
290
296
  res.json(result);
291
297
  } catch (error) {
@@ -21,7 +21,7 @@ async function handler(event) {
21
21
 
22
22
  // If executionId provided (async from API), update existing record
23
23
  if (executionId) {
24
- await commands.updateScriptExecutionStatus(executionId, 'RUNNING');
24
+ await commands.updateAdminProcessState(executionId, 'RUNNING');
25
25
  }
26
26
 
27
27
  const result = await runner.execute(scriptName, params, {
@@ -45,7 +45,7 @@ async function handler(event) {
45
45
  if (executionId) {
46
46
  const commands = createAdminScriptCommands();
47
47
  await commands
48
- .completeScriptExecution(executionId, {
48
+ .completeAdminProcess(executionId, {
49
49
  status: 'FAILED',
50
50
  error: {
51
51
  name: error.name,