@memberjunction/server 2.106.0 → 2.108.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/dist/agents/skip-sdk.d.ts +11 -0
- package/dist/agents/skip-sdk.d.ts.map +1 -1
- package/dist/agents/skip-sdk.js +262 -3
- package/dist/agents/skip-sdk.js.map +1 -1
- package/dist/config.d.ts +60 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +3 -2
- package/dist/context.js.map +1 -1
- package/dist/generated/generated.d.ts +355 -20
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +2220 -154
- package/dist/generated/generated.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -1
- package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
- package/dist/resolvers/AskSkipResolver.js +0 -2
- package/dist/resolvers/AskSkipResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +1 -0
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/services/ScheduledJobsService.d.ts +21 -0
- package/dist/services/ScheduledJobsService.d.ts.map +1 -0
- package/dist/services/ScheduledJobsService.js +105 -0
- package/dist/services/ScheduledJobsService.js.map +1 -0
- package/package.json +34 -30
- package/src/agents/skip-sdk.ts +351 -8
- package/src/config.ts +10 -0
- package/src/context.ts +3 -2
- package/src/generated/generated.ts +1419 -119
- package/src/index.ts +50 -0
- package/src/resolvers/AskSkipResolver.ts +5 -3
- package/src/resolvers/RunAIAgentResolver.ts +1 -0
- package/src/services/ScheduledJobsService.ts +164 -0
package/src/index.ts
CHANGED
|
@@ -40,6 +40,12 @@ LoadCoreEntitiesServerSubClasses(); // prevent tree shaking for this dynamic mod
|
|
|
40
40
|
import { LoadAgentManagementActions } from '@memberjunction/ai-agent-manager-actions';
|
|
41
41
|
LoadAgentManagementActions();
|
|
42
42
|
|
|
43
|
+
import { LoadSchedulingEngine } from '@memberjunction/scheduling-engine';
|
|
44
|
+
LoadSchedulingEngine(); // This also loads drivers
|
|
45
|
+
|
|
46
|
+
import { LoadAllSchedulingActions } from '@memberjunction/scheduling-actions';
|
|
47
|
+
LoadAllSchedulingActions(); // prevent tree shaking for scheduling actions
|
|
48
|
+
|
|
43
49
|
|
|
44
50
|
import { resolve } from 'node:path';
|
|
45
51
|
import { DataSourceInfo, raiseEvent } from './types.js';
|
|
@@ -50,6 +56,7 @@ LoadAIEngine();
|
|
|
50
56
|
LoadAIProviders();
|
|
51
57
|
|
|
52
58
|
import { ExternalChangeDetectorEngine } from '@memberjunction/external-change-detection';
|
|
59
|
+
import { ScheduledJobsService } from './services/ScheduledJobsService.js';
|
|
53
60
|
|
|
54
61
|
const cacheRefreshInterval = configInfo.databaseSettings.metadataCacheRefreshInterval;
|
|
55
62
|
|
|
@@ -302,6 +309,19 @@ export const serve = async (resolverPaths: Array<string>, app = createApp(), opt
|
|
|
302
309
|
// Set up REST endpoints with the configured options and auth middleware
|
|
303
310
|
setupRESTEndpoints(app, restApiConfig, authMiddleware);
|
|
304
311
|
|
|
312
|
+
// Initialize and start scheduled jobs service if enabled
|
|
313
|
+
let scheduledJobsService: ScheduledJobsService | null = null;
|
|
314
|
+
if (configInfo.scheduledJobs?.enabled) {
|
|
315
|
+
try {
|
|
316
|
+
scheduledJobsService = new ScheduledJobsService(configInfo.scheduledJobs);
|
|
317
|
+
await scheduledJobsService.Initialize();
|
|
318
|
+
await scheduledJobsService.Start();
|
|
319
|
+
} catch (error) {
|
|
320
|
+
console.error('❌ Failed to start scheduled jobs service:', error);
|
|
321
|
+
// Don't throw - allow server to start even if scheduled jobs fail
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
305
325
|
if (options?.onBeforeServe) {
|
|
306
326
|
await Promise.resolve(options.onBeforeServe());
|
|
307
327
|
}
|
|
@@ -309,4 +329,34 @@ export const serve = async (resolverPaths: Array<string>, app = createApp(), opt
|
|
|
309
329
|
await new Promise<void>((resolve) => httpServer.listen({ port: graphqlPort }, resolve));
|
|
310
330
|
console.log(`📦 Connected to database: ${dbHost}:${dbPort}/${dbDatabase}`);
|
|
311
331
|
console.log(`🚀 Server ready at http://localhost:${graphqlPort}/`);
|
|
332
|
+
|
|
333
|
+
// Set up graceful shutdown handlers
|
|
334
|
+
const gracefulShutdown = async (signal: string) => {
|
|
335
|
+
console.log(`\n${signal} received, shutting down gracefully...`);
|
|
336
|
+
|
|
337
|
+
// Stop scheduled jobs service
|
|
338
|
+
if (scheduledJobsService?.IsRunning) {
|
|
339
|
+
try {
|
|
340
|
+
await scheduledJobsService.Stop();
|
|
341
|
+
console.log('✅ Scheduled jobs service stopped');
|
|
342
|
+
} catch (error) {
|
|
343
|
+
console.error('❌ Error stopping scheduled jobs service:', error);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Close server
|
|
348
|
+
httpServer.close(() => {
|
|
349
|
+
console.log('✅ HTTP server closed');
|
|
350
|
+
process.exit(0);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// Force close after 10 seconds
|
|
354
|
+
setTimeout(() => {
|
|
355
|
+
console.error('⚠️ Forced shutdown after timeout');
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}, 10000);
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
361
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
312
362
|
};
|
|
@@ -375,7 +375,8 @@ function initializeSkipLearningCycleScheduler() {
|
|
|
375
375
|
return;
|
|
376
376
|
}
|
|
377
377
|
if (!skipConfigInfo.learningCycleEnabled) {
|
|
378
|
-
|
|
378
|
+
// Skip AI Learning Cycles not enabled - disabled logging to reduce startup noise
|
|
379
|
+
// LogStatus('Skip AI Learning Cycles not enabled in configuration');
|
|
379
380
|
return;
|
|
380
381
|
}
|
|
381
382
|
|
|
@@ -428,8 +429,9 @@ function initializeSkipLearningCycleScheduler() {
|
|
|
428
429
|
LogError(`Failed to initialize Skip learning cycle scheduler: ${error}`);
|
|
429
430
|
}
|
|
430
431
|
}
|
|
431
|
-
//
|
|
432
|
-
|
|
432
|
+
// Disabled: Skip AI Learning Cycles no longer used - commented out to prevent startup initialization
|
|
433
|
+
// If needed in the future, uncomment the line below:
|
|
434
|
+
// initializeSkipLearningCycleScheduler();
|
|
433
435
|
|
|
434
436
|
/**
|
|
435
437
|
* Base type for Skip API requests containing common fields
|
|
@@ -136,6 +136,7 @@ export class RunAIAgentResolver extends ResolverBase {
|
|
|
136
136
|
const sanitized: any = {
|
|
137
137
|
success: result.success,
|
|
138
138
|
payload: result.payload,
|
|
139
|
+
suggestedResponses: result.suggestedResponses,
|
|
139
140
|
errorMessage: result.agentRun?.ErrorMessage,
|
|
140
141
|
finalStep: result.agentRun?.FinalStep,
|
|
141
142
|
cancelled: result.agentRun?.Status === 'Cancelled',
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Service module for managing scheduled job lifecycle
|
|
3
|
+
* @module MJServer/services
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { LogError, LogStatus, UserInfo } from '@memberjunction/core';
|
|
7
|
+
import { UserCache } from '@memberjunction/sqlserver-dataprovider';
|
|
8
|
+
import { SchedulingEngine } from '@memberjunction/scheduling-engine';
|
|
9
|
+
import { ScheduledJobsConfig } from '../config.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Service for managing scheduled jobs lifecycle
|
|
13
|
+
* Handles initialization, starting/stopping polling, and graceful shutdown
|
|
14
|
+
*/
|
|
15
|
+
export class ScheduledJobsService {
|
|
16
|
+
private engine: SchedulingEngine;
|
|
17
|
+
private systemUser: UserInfo | null = null;
|
|
18
|
+
private isRunning: boolean = false;
|
|
19
|
+
private config: ScheduledJobsConfig;
|
|
20
|
+
|
|
21
|
+
constructor(config: ScheduledJobsConfig) {
|
|
22
|
+
this.config = config;
|
|
23
|
+
this.engine = SchedulingEngine.Instance;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Initialize the scheduled jobs service
|
|
28
|
+
* Loads metadata and prepares the engine
|
|
29
|
+
*/
|
|
30
|
+
public async Initialize(): Promise<void> {
|
|
31
|
+
if (!this.config.enabled) {
|
|
32
|
+
LogStatus('[ScheduledJobsService] Scheduled jobs are disabled in configuration');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
// Get system user for job execution
|
|
38
|
+
this.systemUser = await this.getSystemUser();
|
|
39
|
+
|
|
40
|
+
if (!this.systemUser) {
|
|
41
|
+
throw new Error(`System user not found with email: ${this.config.systemUserEmail}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Pre-load metadata cache
|
|
45
|
+
await this.engine.Config(false, this.systemUser);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
LogError('[ScheduledJobsService] Failed to initialize', undefined, error);
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Start the scheduled jobs polling
|
|
54
|
+
*/
|
|
55
|
+
public async Start(): Promise<void> {
|
|
56
|
+
if (!this.config.enabled) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (this.isRunning) {
|
|
61
|
+
LogStatus('[ScheduledJobsService] Already running');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!this.systemUser) {
|
|
66
|
+
throw new Error('Service not initialized - call Initialize() first');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
this.engine.StartPolling(this.systemUser);
|
|
71
|
+
this.isRunning = true;
|
|
72
|
+
|
|
73
|
+
// Single consolidated console message
|
|
74
|
+
const jobCount = this.engine.ScheduledJobs.length;
|
|
75
|
+
if (jobCount === 0) {
|
|
76
|
+
console.log(`📅 Scheduled Jobs: No active jobs, polling suspended (will auto-start when jobs are added)`);
|
|
77
|
+
} else {
|
|
78
|
+
const interval = this.engine.ActivePollingInterval;
|
|
79
|
+
if (interval !== null) {
|
|
80
|
+
const intervalDisplay = interval >= 60000
|
|
81
|
+
? `${Math.round(interval / 60000)} minute(s)`
|
|
82
|
+
: `${Math.round(interval / 1000)} second(s)`;
|
|
83
|
+
console.log(`📅 Scheduled Jobs: ${jobCount} active job(s), polling every ${intervalDisplay}`);
|
|
84
|
+
} else {
|
|
85
|
+
// This shouldn't happen if jobCount > 0, but handle it gracefully
|
|
86
|
+
console.log(`📅 Scheduled Jobs: ${jobCount} active job(s), polling interval not set`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} catch (error) {
|
|
90
|
+
LogError('[ScheduledJobsService] Failed to start polling', undefined, error);
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Stop the scheduled jobs polling gracefully
|
|
97
|
+
*/
|
|
98
|
+
public async Stop(): Promise<void> {
|
|
99
|
+
if (!this.isRunning) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
LogStatus('[ScheduledJobsService] Stopping scheduled job polling');
|
|
105
|
+
this.engine.StopPolling();
|
|
106
|
+
this.isRunning = false;
|
|
107
|
+
LogStatus('[ScheduledJobsService] Polling stopped successfully');
|
|
108
|
+
} catch (error) {
|
|
109
|
+
LogError('[ScheduledJobsService] Error stopping polling', undefined, error);
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get the system user for job execution
|
|
116
|
+
* Uses the email configured in scheduledJobs.systemUserEmail
|
|
117
|
+
*/
|
|
118
|
+
private async getSystemUser(): Promise<UserInfo | null> {
|
|
119
|
+
const systemUserEmail = this.config.systemUserEmail;
|
|
120
|
+
|
|
121
|
+
// Search UserCache for system user
|
|
122
|
+
const user = UserCache.Users.find(u =>
|
|
123
|
+
u.Email?.toLowerCase() === systemUserEmail.toLowerCase()
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
if (user) {
|
|
127
|
+
return user;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
LogError(`[ScheduledJobsService] System user not found with email: ${systemUserEmail}`);
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get current service status
|
|
136
|
+
*/
|
|
137
|
+
public GetStatus(): {
|
|
138
|
+
enabled: boolean;
|
|
139
|
+
running: boolean;
|
|
140
|
+
activeJobs: number;
|
|
141
|
+
pollingInterval: number;
|
|
142
|
+
} {
|
|
143
|
+
return {
|
|
144
|
+
enabled: this.config.enabled,
|
|
145
|
+
running: this.isRunning,
|
|
146
|
+
activeJobs: this.engine?.ScheduledJobs?.length || 0,
|
|
147
|
+
pollingInterval: this.engine?.ActivePollingInterval || 0
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check if service is enabled in configuration
|
|
153
|
+
*/
|
|
154
|
+
public get IsEnabled(): boolean {
|
|
155
|
+
return this.config.enabled;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Check if service is currently running
|
|
160
|
+
*/
|
|
161
|
+
public get IsRunning(): boolean {
|
|
162
|
+
return this.isRunning;
|
|
163
|
+
}
|
|
164
|
+
}
|