@friggframework/admin-scripts 2.0.0--canary.517.35ee143.0 → 2.0.0--canary.517.f738cdd.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.
@@ -0,0 +1,56 @@
1
+ # PR #517 Comment Tracker
2
+
3
+ ## ✅ ADDRESSED - Ready to Reply
4
+
5
+ | # | File:Line | Original Comment | What We Did | Reply |
6
+ |---|-----------|------------------|-------------|-------|
7
+ | 1 | `admin-frigg-commands.js:15` | "I don't think this should even exist, we're just duplicating methods" | Renamed to `AdminScriptContext`, clarified as facade pattern | Renamed to `AdminScriptContext`. It's a facade - wraps repositories so scripts have one API instead of multiple imports. Open to discussing alternatives. |
8
+ | 2 | `admin-script-base.js:94` | "Commands should come via constructor" | Changed to constructor injection | Done. Context now passed via constructor, scripts access via `this.context`. |
9
+ | 3 | `admin-script-base.js:109` | "logging does not belong here" | Removed logging from base class | Removed. Scripts use `this.context.log()` which persists to admin process record. |
10
+ | 4 | `admin-script-base.js:54` | "I would rename to requireIntegrationInstance" | Already renamed | Done - already renamed in current code. |
11
+ | 5 | `admin-script-base.js:56` | "This is just a duplication of the static Definition" | Cleaned up display object | Cleaned up. `display` now only holds UI overrides (category, icon). Label/description fall back to top-level via static methods. |
12
+ | 6 | `schedule-management-use-case.js:1` | "A use case should have single entry point" | Already split in previous session | Already split into `UpsertScheduleUseCase`, `DeleteScheduleUseCase`, `GetEffectiveScheduleUseCase`. |
13
+ | 7 | `package.json:20` | "chai and sinon should slowly be pushed away" | Sinon removed in previous session | Sinon already removed. Will remove chai too. |
14
+
15
+ ---
16
+
17
+ ## 📝 NEEDS RESPONSE ONLY - No Code Change Required
18
+
19
+ | # | File:Line | Original Comment | Reply |
20
+ |---|-----------|------------------|-------|
21
+ | 8 | `admin-frigg-commands.js:160` | "this does not belong here" (queueScript) | queueScript enables self-queuing pattern (fan-out, pagination, retries). It's here so scripts don't need queue internals. Could move to separate utility if preferred. |
22
+ | 9 | `admin-script-base.js:39` | "why do we need the source?" | Distinguishes builtin vs user-defined scripts. UI can filter differently, builtins could have special handling. Could remove if not needed. |
23
+ | 10 | `admin-script-base.js:42` | "what's the idea with these schemas?" | Optional JSON Schema for validation/documentation. Could wire to OpenAPI or dynamic UI forms. Not critical for v1 - could remove and add later. |
24
+ | 11 | `admin-script-base.js:46` | "enabled property confusion" | Agreed the matrix is confusing. Intent: `schedule.enabled` controls auto-trigger independent of registration. Could simplify to just use presence in appDefinition. |
25
+ | 12 | `admin-script-base.js:52` | "Do we have retry logic in place already?" | Not yet - placeholder for Phase 2. Could remove until we build it. |
26
+ | 13 | `admin-script-base.js:81` | "What is the executionId?" | ID of AdminProcess record tracking this execution. Used to persist logs and update status. Created before script runs, passed to constructor. |
27
+ | 14 | `schedule-management-use-case.js:89` | "why save to database if EventBridge is source of truth?" | Database stores user's config override. EventBridge is execution engine. On deploy, we sync DB to EventBridge. Tracks user config vs code default. |
28
+
29
+ ---
30
+
31
+ ## 🔧 OUTSTANDING - Needs Code Changes
32
+
33
+ | # | File:Line | Original Comment | Task |
34
+ |---|-----------|------------------|------|
35
+ | 15 | `docs/architecture-decisions/005-admin-script-runner.md:71` | "What is 'frigg' in this parameter?" | Update ADR - rename `frigg` to `context` throughout |
36
+ | 16 | `package.json:22` | "We already use nock for http request mocking" | Remove msw if present, use nock consistently |
37
+ | 17 | `package.json:12` | "why mongoose?" | Check if mongoose needed or can be removed |
38
+ | 18 | `schedule-management-use-case.js:109` | "leaking AWS specifics" | Abstract behind SchedulerAdapter, remove EventBridge references from use case |
39
+ | 19 | `schedule-management-use-case.js:138` | "should not mention EventBridge here" | Same as above |
40
+ | 20 | `adapters/aws-scheduler-adapter.js:30` | "should not infer/guess/default any variable" | Remove defaults, require explicit config |
41
+ | 21 | `adapters/scheduler-adapter-factory.js:48` | "env var confusion, prefer appDefinition" | Move scheduler config to appDefinition |
42
+ | 22 | `script-runner.js:36` | "we should not assume default values" | Remove defaults, require explicit values |
43
+ | 23 | `dry-run-http-interceptor.js:1` | "I don't understand why this is needed" | Explain or remove - was for intercepting HTTP in dry-run mode |
44
+ | 24 | `dry-run-repository-wrapper.js:1` | "This is smelly" | Review/remove - was for wrapping repos in dry-run mode |
45
+ | 25 | `.github/workflows/release.yml:11` | "why do we need those?" | Check release workflow changes |
46
+
47
+ ---
48
+
49
+ ## ❓ NEEDS DISCUSSION - Architectural Decisions
50
+
51
+ | # | File:Line | Original Comment | Decision Needed |
52
+ |---|-----------|------------------|-----------------|
53
+ | 26 | `admin-script-base.js:39` | source field | Keep BUILTIN/USER_DEFINED or remove? |
54
+ | 27 | `admin-script-base.js:42` | inputSchema/outputSchema | Keep for future or remove for now? |
55
+ | 28 | `admin-script-base.js:46` | schedule.enabled | Simplify to just appDefinition presence? |
56
+ | 29 | `admin-script-base.js:52` | maxRetries | Remove placeholder or keep? |
package/index.js CHANGED
@@ -8,7 +8,13 @@
8
8
  // Application Services
9
9
  const { ScriptFactory, getScriptFactory, createScriptFactory } = require('./src/application/script-factory');
10
10
  const { AdminScriptBase } = require('./src/application/admin-script-base');
11
- const { AdminFriggCommands, createAdminFriggCommands } = require('./src/application/admin-frigg-commands');
11
+ const {
12
+ AdminScriptContext,
13
+ createAdminScriptContext,
14
+ // Legacy aliases (deprecated)
15
+ AdminFriggCommands,
16
+ createAdminFriggCommands,
17
+ } = require('./src/application/admin-frigg-commands');
12
18
  const { ScriptRunner, createScriptRunner } = require('./src/application/script-runner');
13
19
 
14
20
  // Infrastructure
@@ -39,6 +45,9 @@ module.exports = {
39
45
  ScriptFactory,
40
46
  getScriptFactory,
41
47
  createScriptFactory,
48
+ AdminScriptContext,
49
+ createAdminScriptContext,
50
+ // Legacy aliases (deprecated)
42
51
  AdminFriggCommands,
43
52
  createAdminFriggCommands,
44
53
  ScriptRunner,
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@friggframework/admin-scripts",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.517.35ee143.0",
4
+ "version": "2.0.0--canary.517.f738cdd.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.35ee143.0",
8
+ "@friggframework/core": "2.0.0--canary.517.f738cdd.0",
9
9
  "bcryptjs": "^2.4.3",
10
10
  "express": "^4.18.2",
11
11
  "lodash": "4.17.21",
@@ -13,9 +13,9 @@
13
13
  "uuid": "^9.0.1"
14
14
  },
15
15
  "devDependencies": {
16
- "@friggframework/eslint-config": "2.0.0--canary.517.35ee143.0",
17
- "@friggframework/prettier-config": "2.0.0--canary.517.35ee143.0",
18
- "@friggframework/test": "2.0.0--canary.517.35ee143.0",
16
+ "@friggframework/eslint-config": "2.0.0--canary.517.f738cdd.0",
17
+ "@friggframework/prettier-config": "2.0.0--canary.517.f738cdd.0",
18
+ "@friggframework/test": "2.0.0--canary.517.f738cdd.0",
19
19
  "eslint": "^8.22.0",
20
20
  "jest": "^29.7.0",
21
21
  "prettier": "^2.7.1",
@@ -46,5 +46,5 @@
46
46
  "maintenance",
47
47
  "operations"
48
48
  ],
49
- "gitHead": "35ee143ec0654d0935aee59bf62b882f5f27d8b7"
49
+ "gitHead": "f738cdd2e74529b3719a974c44e39e08cbf24313"
50
50
  }
@@ -31,9 +31,8 @@ describe('AdminScriptBase', () => {
31
31
  requireIntegrationInstance: true,
32
32
  },
33
33
  display: {
34
- label: 'Test Script',
35
- description: 'For testing',
36
34
  category: 'testing',
35
+ icon: 'test-icon',
37
36
  },
38
37
  };
39
38
  }
@@ -45,6 +44,15 @@ describe('AdminScriptBase', () => {
45
44
  expect(TestScript.Definition.schedule.enabled).toBe(true);
46
45
  expect(TestScript.Definition.config.timeout).toBe(600000);
47
46
  });
47
+
48
+ it('should have clean display object without redundant fields', () => {
49
+ // Default display should only have UI-specific fields
50
+ expect(AdminScriptBase.Definition.display).toBeDefined();
51
+ expect(AdminScriptBase.Definition.display.category).toBe('maintenance');
52
+ // Should NOT have redundant label/description
53
+ expect(AdminScriptBase.Definition.display.label).toBeUndefined();
54
+ expect(AdminScriptBase.Definition.display.description).toBeUndefined();
55
+ });
48
56
  });
49
57
 
50
58
  describe('Static methods', () => {
@@ -90,18 +98,68 @@ describe('AdminScriptBase', () => {
90
98
  source: 'USER_DEFINED',
91
99
  });
92
100
  });
101
+
102
+ it('getDisplayLabel() should return display.label or fall back to name', () => {
103
+ class ScriptWithLabel extends AdminScriptBase {
104
+ static Definition = {
105
+ name: 'my-script',
106
+ version: '1.0.0',
107
+ description: 'test',
108
+ display: { label: 'My Custom Label' },
109
+ };
110
+ }
111
+
112
+ class ScriptWithoutLabel extends AdminScriptBase {
113
+ static Definition = {
114
+ name: 'another-script',
115
+ version: '1.0.0',
116
+ description: 'test',
117
+ };
118
+ }
119
+
120
+ expect(ScriptWithLabel.getDisplayLabel()).toBe('My Custom Label');
121
+ expect(ScriptWithoutLabel.getDisplayLabel()).toBe('another-script');
122
+ });
123
+
124
+ it('getDisplayDescription() should return display.description or fall back to description', () => {
125
+ class ScriptWithDisplayDesc extends AdminScriptBase {
126
+ static Definition = {
127
+ name: 'my-script',
128
+ version: '1.0.0',
129
+ description: 'Technical description',
130
+ display: { description: 'User-friendly description' },
131
+ };
132
+ }
133
+
134
+ class ScriptWithoutDisplayDesc extends AdminScriptBase {
135
+ static Definition = {
136
+ name: 'another-script',
137
+ version: '1.0.0',
138
+ description: 'Technical description',
139
+ };
140
+ }
141
+
142
+ expect(ScriptWithDisplayDesc.getDisplayDescription()).toBe('User-friendly description');
143
+ expect(ScriptWithoutDisplayDesc.getDisplayDescription()).toBe('Technical description');
144
+ });
93
145
  });
94
146
 
95
147
  describe('Constructor', () => {
96
148
  it('should initialize with default values', () => {
97
149
  const script = new AdminScriptBase();
98
150
 
151
+ expect(script.context).toBeNull();
99
152
  expect(script.executionId).toBeNull();
100
- expect(script.logs).toEqual([]);
101
- expect(script._startTime).toBeNull();
102
153
  expect(script.integrationFactory).toBeNull();
103
154
  });
104
155
 
156
+ it('should accept context parameter', () => {
157
+ const mockContext = { log: jest.fn() };
158
+ const script = new AdminScriptBase({ context: mockContext });
159
+
160
+ expect(script.context).toBe(mockContext);
161
+ });
162
+
105
163
  it('should accept executionId parameter', () => {
106
164
  const script = new AdminScriptBase({ executionId: 'exec_123' });
107
165
 
@@ -117,13 +175,16 @@ describe('AdminScriptBase', () => {
117
175
  expect(script.integrationFactory).toBe(mockFactory);
118
176
  });
119
177
 
120
- it('should accept both executionId and integrationFactory', () => {
178
+ it('should accept all parameters together', () => {
179
+ const mockContext = { log: jest.fn() };
121
180
  const mockFactory = { mock: true };
122
181
  const script = new AdminScriptBase({
182
+ context: mockContext,
123
183
  executionId: 'exec_456',
124
184
  integrationFactory: mockFactory,
125
185
  });
126
186
 
187
+ expect(script.context).toBe(mockContext);
127
188
  expect(script.executionId).toBe('exec_456');
128
189
  expect(script.integrationFactory).toBe(mockFactory);
129
190
  });
@@ -133,12 +194,12 @@ describe('AdminScriptBase', () => {
133
194
  it('should throw error when not implemented by subclass', async () => {
134
195
  const script = new AdminScriptBase();
135
196
 
136
- await expect(script.execute({}, {})).rejects.toThrow(
197
+ await expect(script.execute({})).rejects.toThrow(
137
198
  'AdminScriptBase.execute() must be implemented by subclass'
138
199
  );
139
200
  });
140
201
 
141
- it('should allow child classes to implement execute()', async () => {
202
+ it('should allow child classes to implement execute() with params only', async () => {
142
203
  class TestScript extends AdminScriptBase {
143
204
  static Definition = {
144
205
  name: 'test',
@@ -146,90 +207,45 @@ describe('AdminScriptBase', () => {
146
207
  description: 'test',
147
208
  };
148
209
 
149
- async execute(frigg, params) {
210
+ async execute(params) {
150
211
  return { result: 'success', params };
151
212
  }
152
213
  }
153
214
 
154
215
  const script = new TestScript();
155
- const frigg = {};
156
216
  const params = { foo: 'bar' };
157
217
 
158
- const result = await script.execute(frigg, params);
218
+ const result = await script.execute(params);
159
219
 
160
220
  expect(result.result).toBe('success');
161
221
  expect(result.params).toEqual({ foo: 'bar' });
162
222
  });
163
- });
164
-
165
- describe('Logging methods', () => {
166
- it('log() should create log entry with timestamp', () => {
167
- const script = new AdminScriptBase();
168
- const beforeTime = new Date().toISOString();
169
-
170
- const entry = script.log('info', 'Test message', { key: 'value' });
171
-
172
- const afterTime = new Date().toISOString();
173
-
174
- expect(entry.level).toBe('info');
175
- expect(entry.message).toBe('Test message');
176
- expect(entry.data).toEqual({ key: 'value' });
177
- expect(entry.timestamp).toBeDefined();
178
- expect(entry.timestamp >= beforeTime).toBe(true);
179
- expect(entry.timestamp <= afterTime).toBe(true);
180
- });
181
-
182
- it('log() should add entry to logs array', () => {
183
- const script = new AdminScriptBase();
184
-
185
- script.log('info', 'First');
186
- script.log('error', 'Second');
187
- script.log('warn', 'Third');
188
-
189
- const logs = script.getLogs();
190
-
191
- expect(logs).toHaveLength(3);
192
- expect(logs[0].message).toBe('First');
193
- expect(logs[1].message).toBe('Second');
194
- expect(logs[2].message).toBe('Third');
195
- });
196
-
197
- it('log() should default data to empty object', () => {
198
- const script = new AdminScriptBase();
199
-
200
- const entry = script.log('info', 'No data');
201
223
 
202
- expect(entry.data).toEqual({});
203
- });
204
-
205
- it('getLogs() should return logs array', () => {
206
- const script = new AdminScriptBase();
207
-
208
- script.log('info', 'Message 1');
209
- script.log('error', 'Message 2');
210
-
211
- const logs = script.getLogs();
212
-
213
- expect(logs).toHaveLength(2);
214
- expect(logs[0].level).toBe('info');
215
- expect(logs[1].level).toBe('error');
216
- });
224
+ it('should access context via this.context', async () => {
225
+ class TestScript extends AdminScriptBase {
226
+ static Definition = {
227
+ name: 'test',
228
+ version: '1.0.0',
229
+ description: 'test',
230
+ };
217
231
 
218
- it('clearLogs() should empty logs array', () => {
219
- const script = new AdminScriptBase();
232
+ async execute(params) {
233
+ this.context.log('info', 'Starting');
234
+ return { success: true };
235
+ }
236
+ }
220
237
 
221
- script.log('info', 'Message 1');
222
- script.log('info', 'Message 2');
223
- expect(script.getLogs()).toHaveLength(2);
238
+ const mockContext = { log: jest.fn() };
239
+ const script = new TestScript({ context: mockContext });
224
240
 
225
- script.clearLogs();
241
+ await script.execute({});
226
242
 
227
- expect(script.getLogs()).toHaveLength(0);
243
+ expect(mockContext.log).toHaveBeenCalledWith('info', 'Starting');
228
244
  });
229
245
  });
230
246
 
231
247
  describe('Integration with child classes', () => {
232
- it('should support full lifecycle', async () => {
248
+ it('should support full lifecycle with context injection', async () => {
233
249
  class MyScript extends AdminScriptBase {
234
250
  static Definition = {
235
251
  name: 'my-script',
@@ -240,34 +256,34 @@ describe('AdminScriptBase', () => {
240
256
  },
241
257
  };
242
258
 
243
- async execute(frigg, params) {
244
- this.log('info', 'Starting execution');
245
- this.log('debug', 'Processing', params);
259
+ async execute(params) {
260
+ this.context.log('info', 'Starting execution');
261
+ this.context.log('debug', 'Processing', params);
246
262
 
247
263
  if (this.integrationFactory) {
248
- this.log('info', 'Integration factory available');
264
+ this.context.log('info', 'Integration factory available');
249
265
  }
250
266
 
251
267
  return { processed: true };
252
268
  }
253
269
  }
254
270
 
271
+ const mockContext = { log: jest.fn() };
255
272
  const mockFactory = { getInstanceById: jest.fn() };
256
273
  const script = new MyScript({
274
+ context: mockContext,
257
275
  executionId: 'exec_789',
258
276
  integrationFactory: mockFactory,
259
277
  });
260
278
 
261
- const frigg = {};
262
- const result = await script.execute(frigg, { test: 'data' });
279
+ const result = await script.execute({ test: 'data' });
263
280
 
264
281
  expect(result).toEqual({ processed: true });
265
282
 
266
- const logs = script.getLogs();
267
- expect(logs).toHaveLength(3);
268
- expect(logs[0].message).toBe('Starting execution');
269
- expect(logs[1].message).toBe('Processing');
270
- expect(logs[2].message).toBe('Integration factory available');
283
+ expect(mockContext.log).toHaveBeenCalledTimes(3);
284
+ expect(mockContext.log).toHaveBeenCalledWith('info', 'Starting execution');
285
+ expect(mockContext.log).toHaveBeenCalledWith('debug', 'Processing', { test: 'data' });
286
+ expect(mockContext.log).toHaveBeenCalledWith('info', 'Integration factory available');
271
287
  });
272
288
  });
273
289
  });
@@ -1,18 +1,6 @@
1
1
  const { QueuerUtil } = require('@friggframework/core/queues');
2
2
 
3
- /**
4
- * AdminFriggCommands
5
- *
6
- * Helper API for admin scripts. Provides:
7
- * - Database access via repositories
8
- * - Integration instantiation (optional)
9
- * - Logging utilities
10
- * - Queue operations for self-queuing pattern
11
- *
12
- * Follows lazy-loading pattern for repositories to avoid circular dependencies
13
- * and unnecessary initialization.
14
- */
15
- class AdminFriggCommands {
3
+ class AdminScriptContext {
16
4
  constructor(params = {}) {
17
5
  this.executionId = params.executionId || null;
18
6
  this.logs = [];
@@ -151,12 +139,8 @@ class AdminFriggCommands {
151
139
  });
152
140
  }
153
141
 
154
- // ==================== QUEUE OPERATIONS (Self-Queuing Pattern) ====================
142
+ // ==================== QUEUE OPERATIONS ====================
155
143
 
156
- /**
157
- * Queue a script for execution
158
- * Used for self-queuing pattern with long-running scripts
159
- */
160
144
  async queueScript(scriptName, params = {}) {
161
145
  const queueUrl = process.env.ADMIN_SCRIPT_QUEUE_URL;
162
146
  if (!queueUrl) {
@@ -176,9 +160,6 @@ class AdminFriggCommands {
176
160
  this.log('info', `Queued continuation for ${scriptName}`, { params });
177
161
  }
178
162
 
179
- /**
180
- * Queue multiple scripts in a batch
181
- */
182
163
  async queueScriptBatch(entries) {
183
164
  const queueUrl = process.env.ADMIN_SCRIPT_QUEUE_URL;
184
165
  if (!queueUrl) {
@@ -230,13 +211,20 @@ class AdminFriggCommands {
230
211
  }
231
212
 
232
213
  /**
233
- * Create AdminFriggCommands instance
214
+ * Create AdminScriptContext instance
234
215
  */
235
- function createAdminFriggCommands(params = {}) {
236
- return new AdminFriggCommands(params);
216
+ function createAdminScriptContext(params = {}) {
217
+ return new AdminScriptContext(params);
237
218
  }
238
219
 
220
+ // Legacy aliases for backwards compatibility
221
+ const AdminFriggCommands = AdminScriptContext;
222
+ const createAdminFriggCommands = createAdminScriptContext;
223
+
239
224
  module.exports = {
225
+ AdminScriptContext,
226
+ createAdminScriptContext,
227
+ // Legacy exports (deprecated)
240
228
  AdminFriggCommands,
241
229
  createAdminFriggCommands,
242
230
  };
@@ -1,63 +1,27 @@
1
- const { createAdminProcessRepository } = require('@friggframework/core/admin-scripts/repositories/admin-process-repository-factory');
2
-
3
- /**
4
- * Admin Script Base Class
5
- *
6
- * Base class for all admin scripts. Provides:
7
- * - Standard script definition pattern
8
- * - Repository access
9
- * - Logging helpers
10
- * - Integration factory support (optional)
11
- *
12
- * Usage:
13
- * ```javascript
14
- * class MyScript extends AdminScriptBase {
15
- * static Definition = {
16
- * name: 'my-script',
17
- * version: '1.0.0',
18
- * description: 'Does something useful',
19
- * ...
20
- * };
21
- *
22
- * async execute(frigg, params) {
23
- * // Your script logic here
24
- * }
25
- * }
26
- * ```
27
- */
28
1
  class AdminScriptBase {
29
- /**
30
- * CHILDREN SHOULD SPECIFY A DEFINITION FOR THE SCRIPT
31
- * Pattern matches IntegrationBase.Definition
32
- */
33
2
  static Definition = {
34
- name: 'Script Name', // Required: unique identifier
35
- version: '0.0.0', // Required: semver for migrations
36
- description: 'What this script does', // Required: human-readable
37
-
38
- // Script-specific properties
3
+ name: 'Script Name',
4
+ version: '0.0.0',
5
+ description: 'What this script does',
39
6
  source: 'USER_DEFINED', // 'BUILTIN' | 'USER_DEFINED'
40
7
 
41
- inputSchema: null, // Optional: JSON Schema for params
42
- outputSchema: null, // Optional: JSON Schema for results
8
+ inputSchema: null,
9
+ outputSchema: null,
43
10
 
44
11
  schedule: {
45
- // Optional: Phase 2
46
12
  enabled: false,
47
- cronExpression: null, // 'cron(0 12 * * ? *)'
13
+ cronExpression: null,
48
14
  },
49
15
 
50
16
  config: {
51
- timeout: 300000, // Default 5 min (ms)
17
+ timeout: 300000,
52
18
  maxRetries: 0,
53
- requireIntegrationInstance: false, // Hint: does script need to instantiate integrations?
19
+ requireIntegrationInstance: false,
54
20
  },
55
21
 
56
22
  display: {
57
- // For future UI
58
- label: 'Script Name',
59
- description: '',
60
- category: 'maintenance', // 'maintenance' | 'healing' | 'sync' | 'custom'
23
+ category: 'maintenance',
24
+ icon: null,
61
25
  },
62
26
  };
63
27
 
@@ -73,63 +37,22 @@ class AdminScriptBase {
73
37
  return this.Definition;
74
38
  }
75
39
 
76
- /**
77
- * Constructor receives dependencies
78
- * Pattern matches IntegrationBase constructor
79
- */
80
- constructor(params = {}) {
81
- this.executionId = params.executionId || null;
82
- this.logs = [];
83
- this._startTime = null;
84
-
85
- // OPTIONAL: Integration factory for scripts that need it
86
- this.integrationFactory = params.integrationFactory || null;
87
-
88
- // OPTIONAL: Injected repositories (for testing or custom implementations)
89
- this.adminProcessRepository = params.adminProcessRepository || null;
90
- }
91
-
92
- /**
93
- * CHILDREN MUST IMPLEMENT THIS METHOD
94
- * @param {AdminFriggCommands} frigg - Helper commands object
95
- * @param {Object} params - Script parameters (validated against inputSchema)
96
- * @returns {Promise<Object>} - Script results (validated against outputSchema)
97
- */
98
- async execute(frigg, params) {
99
- throw new Error('AdminScriptBase.execute() must be implemented by subclass');
40
+ static getDisplayLabel() {
41
+ return this.Definition.display?.label || this.Definition.name;
100
42
  }
101
43
 
102
- /**
103
- * Logging helper
104
- * @param {string} level - Log level (info, warn, error, debug)
105
- * @param {string} message - Log message
106
- * @param {Object} data - Additional data
107
- * @returns {Object} Log entry
108
- */
109
- log(level, message, data = {}) {
110
- const entry = {
111
- level,
112
- message,
113
- data,
114
- timestamp: new Date().toISOString(),
115
- };
116
- this.logs.push(entry);
117
- return entry;
44
+ static getDisplayDescription() {
45
+ return this.Definition.display?.description || this.Definition.description;
118
46
  }
119
47
 
120
- /**
121
- * Get all logs
122
- * @returns {Array} Log entries
123
- */
124
- getLogs() {
125
- return this.logs;
48
+ constructor(params = {}) {
49
+ this.context = params.context || null;
50
+ this.executionId = params.executionId || null;
51
+ this.integrationFactory = params.integrationFactory || null;
126
52
  }
127
53
 
128
- /**
129
- * Clear all logs
130
- */
131
- clearLogs() {
132
- this.logs = [];
54
+ async execute(params) {
55
+ throw new Error('AdminScriptBase.execute() must be implemented by subclass');
133
56
  }
134
57
  }
135
58
 
@@ -1,5 +1,5 @@
1
1
  const { getScriptFactory } = require('./script-factory');
2
- const { createAdminFriggCommands } = require('./admin-frigg-commands');
2
+ const { createAdminScriptContext } = require('./admin-frigg-commands');
3
3
  const { createAdminScriptCommands } = require('@friggframework/core/application/commands/admin-script-commands');
4
4
 
5
5
  /**
@@ -7,8 +7,7 @@ const { createAdminScriptCommands } = require('@friggframework/core/application/
7
7
  *
8
8
  * Orchestrates script execution with:
9
9
  * - Execution record creation
10
- * - Script instantiation
11
- * - AdminFriggCommands injection
10
+ * - Script instantiation with context injection
12
11
  * - Error handling
13
12
  * - Status updates
14
13
  */
@@ -71,20 +70,21 @@ class ScriptRunner {
71
70
  try {
72
71
  await this.commands.updateAdminProcessState(executionId, 'RUNNING');
73
72
 
74
- // Create frigg commands for the script
75
- const frigg = createAdminFriggCommands({
73
+ // Create context for the script (facade over repositories, queue, logging)
74
+ const context = createAdminScriptContext({
76
75
  executionId,
77
76
  integrationFactory: this.integrationFactory,
78
77
  });
79
78
 
80
- // Create script instance
79
+ // Create script instance with context injected via constructor
81
80
  const script = this.scriptFactory.createInstance(scriptName, {
81
+ context,
82
82
  executionId,
83
83
  integrationFactory: this.integrationFactory,
84
84
  });
85
85
 
86
86
  // Execute the script
87
- const output = await script.execute(frigg, params);
87
+ const output = await script.execute(params);
88
88
 
89
89
  // Calculate metrics
90
90
  const endTime = new Date();