@flowdot.ai/daemon 1.0.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/LICENSE +45 -0
- package/README.md +51 -0
- package/dist/goals/DependencyResolver.d.ts +54 -0
- package/dist/goals/DependencyResolver.js +329 -0
- package/dist/goals/ErrorRecovery.d.ts +133 -0
- package/dist/goals/ErrorRecovery.js +489 -0
- package/dist/goals/GoalApiClient.d.ts +81 -0
- package/dist/goals/GoalApiClient.js +743 -0
- package/dist/goals/GoalCache.d.ts +65 -0
- package/dist/goals/GoalCache.js +243 -0
- package/dist/goals/GoalCommsHandler.d.ts +150 -0
- package/dist/goals/GoalCommsHandler.js +378 -0
- package/dist/goals/GoalExporter.d.ts +164 -0
- package/dist/goals/GoalExporter.js +318 -0
- package/dist/goals/GoalImporter.d.ts +107 -0
- package/dist/goals/GoalImporter.js +345 -0
- package/dist/goals/GoalManager.d.ts +110 -0
- package/dist/goals/GoalManager.js +535 -0
- package/dist/goals/GoalReporter.d.ts +105 -0
- package/dist/goals/GoalReporter.js +534 -0
- package/dist/goals/GoalScheduler.d.ts +102 -0
- package/dist/goals/GoalScheduler.js +209 -0
- package/dist/goals/GoalValidator.d.ts +72 -0
- package/dist/goals/GoalValidator.js +657 -0
- package/dist/goals/MetaGoalEnforcer.d.ts +111 -0
- package/dist/goals/MetaGoalEnforcer.js +536 -0
- package/dist/goals/MilestoneBreaker.d.ts +74 -0
- package/dist/goals/MilestoneBreaker.js +348 -0
- package/dist/goals/PermissionBridge.d.ts +109 -0
- package/dist/goals/PermissionBridge.js +326 -0
- package/dist/goals/ProgressTracker.d.ts +113 -0
- package/dist/goals/ProgressTracker.js +324 -0
- package/dist/goals/ReviewScheduler.d.ts +106 -0
- package/dist/goals/ReviewScheduler.js +360 -0
- package/dist/goals/TaskExecutor.d.ts +116 -0
- package/dist/goals/TaskExecutor.js +370 -0
- package/dist/goals/TaskFeedback.d.ts +126 -0
- package/dist/goals/TaskFeedback.js +402 -0
- package/dist/goals/TaskGenerator.d.ts +75 -0
- package/dist/goals/TaskGenerator.js +329 -0
- package/dist/goals/TaskQueue.d.ts +84 -0
- package/dist/goals/TaskQueue.js +331 -0
- package/dist/goals/TaskSanitizer.d.ts +61 -0
- package/dist/goals/TaskSanitizer.js +464 -0
- package/dist/goals/errors.d.ts +116 -0
- package/dist/goals/errors.js +299 -0
- package/dist/goals/index.d.ts +24 -0
- package/dist/goals/index.js +23 -0
- package/dist/goals/types.d.ts +395 -0
- package/dist/goals/types.js +230 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/loop/DaemonIPC.d.ts +67 -0
- package/dist/loop/DaemonIPC.js +358 -0
- package/dist/loop/IntervalParser.d.ts +39 -0
- package/dist/loop/IntervalParser.js +217 -0
- package/dist/loop/LoopDaemon.d.ts +123 -0
- package/dist/loop/LoopDaemon.js +1821 -0
- package/dist/loop/LoopExecutor.d.ts +93 -0
- package/dist/loop/LoopExecutor.js +326 -0
- package/dist/loop/LoopManager.d.ts +79 -0
- package/dist/loop/LoopManager.js +476 -0
- package/dist/loop/LoopScheduler.d.ts +69 -0
- package/dist/loop/LoopScheduler.js +329 -0
- package/dist/loop/LoopStore.d.ts +57 -0
- package/dist/loop/LoopStore.js +406 -0
- package/dist/loop/LoopValidator.d.ts +55 -0
- package/dist/loop/LoopValidator.js +603 -0
- package/dist/loop/errors.d.ts +115 -0
- package/dist/loop/errors.js +312 -0
- package/dist/loop/index.d.ts +11 -0
- package/dist/loop/index.js +10 -0
- package/dist/loop/notifications/Notifier.d.ts +28 -0
- package/dist/loop/notifications/Notifier.js +78 -0
- package/dist/loop/notifications/SlackNotifier.d.ts +28 -0
- package/dist/loop/notifications/SlackNotifier.js +203 -0
- package/dist/loop/notifications/TerminalNotifier.d.ts +18 -0
- package/dist/loop/notifications/TerminalNotifier.js +72 -0
- package/dist/loop/notifications/WebhookNotifier.d.ts +24 -0
- package/dist/loop/notifications/WebhookNotifier.js +123 -0
- package/dist/loop/notifications/index.d.ts +24 -0
- package/dist/loop/notifications/index.js +109 -0
- package/dist/loop/types.d.ts +280 -0
- package/dist/loop/types.js +222 -0
- package/package.json +92 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
const noopLogger = {
|
|
3
|
+
debug: () => { },
|
|
4
|
+
info: () => { },
|
|
5
|
+
warn: () => { },
|
|
6
|
+
error: () => { },
|
|
7
|
+
};
|
|
8
|
+
export class GoalScheduler extends EventEmitter {
|
|
9
|
+
getDueGoals;
|
|
10
|
+
markGoalStarted;
|
|
11
|
+
markGoalCompleted;
|
|
12
|
+
executeGoal;
|
|
13
|
+
pollIntervalMs;
|
|
14
|
+
maxConcurrentExecutions;
|
|
15
|
+
logger;
|
|
16
|
+
pollTimer = null;
|
|
17
|
+
isPolling = false;
|
|
18
|
+
activeExecutions = new Map();
|
|
19
|
+
started = false;
|
|
20
|
+
startedAt = null;
|
|
21
|
+
totalPollCount = 0;
|
|
22
|
+
totalExecutionCount = 0;
|
|
23
|
+
totalSuccessCount = 0;
|
|
24
|
+
totalFailureCount = 0;
|
|
25
|
+
constructor(options) {
|
|
26
|
+
super();
|
|
27
|
+
this.getDueGoals = options.getDueGoals;
|
|
28
|
+
this.markGoalStarted = options.markGoalStarted;
|
|
29
|
+
this.markGoalCompleted = options.markGoalCompleted;
|
|
30
|
+
this.executeGoal = options.executeGoal;
|
|
31
|
+
this.pollIntervalMs = options.pollIntervalMs ?? 60000;
|
|
32
|
+
this.maxConcurrentExecutions = options.maxConcurrentExecutions ?? 3;
|
|
33
|
+
this.logger = options.logger ?? noopLogger;
|
|
34
|
+
}
|
|
35
|
+
start() {
|
|
36
|
+
if (this.started) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
this.started = true;
|
|
40
|
+
this.startedAt = new Date();
|
|
41
|
+
this.logger.info('GOAL_SCHEDULER', 'Starting goal scheduler', {
|
|
42
|
+
pollIntervalMs: this.pollIntervalMs,
|
|
43
|
+
maxConcurrentExecutions: this.maxConcurrentExecutions,
|
|
44
|
+
});
|
|
45
|
+
this.poll().catch((error) => {
|
|
46
|
+
this.emit('error', error, { context: 'initial-poll' });
|
|
47
|
+
});
|
|
48
|
+
this.pollTimer = setInterval(() => {
|
|
49
|
+
this.poll().catch((error) => {
|
|
50
|
+
this.emit('error', error, { context: 'scheduled-poll' });
|
|
51
|
+
});
|
|
52
|
+
}, this.pollIntervalMs);
|
|
53
|
+
}
|
|
54
|
+
stop() {
|
|
55
|
+
if (!this.started) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.logger.info('GOAL_SCHEDULER', 'Stopping goal scheduler', {
|
|
59
|
+
activeExecutions: this.activeExecutions.size,
|
|
60
|
+
});
|
|
61
|
+
if (this.pollTimer) {
|
|
62
|
+
clearInterval(this.pollTimer);
|
|
63
|
+
this.pollTimer = null;
|
|
64
|
+
}
|
|
65
|
+
this.started = false;
|
|
66
|
+
}
|
|
67
|
+
isRunning() {
|
|
68
|
+
return this.started;
|
|
69
|
+
}
|
|
70
|
+
async poll() {
|
|
71
|
+
if (this.isPolling) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.isPolling = true;
|
|
75
|
+
this.totalPollCount++;
|
|
76
|
+
this.emit('poll-started');
|
|
77
|
+
try {
|
|
78
|
+
if (this.activeExecutions.size >= this.maxConcurrentExecutions) {
|
|
79
|
+
this.logger.debug('GOAL_SCHEDULER', 'Max concurrent executions reached', {
|
|
80
|
+
active: this.activeExecutions.size,
|
|
81
|
+
max: this.maxConcurrentExecutions,
|
|
82
|
+
});
|
|
83
|
+
this.emit('poll-completed', 0);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const dueGoals = await this.getDueGoals();
|
|
87
|
+
this.logger.debug('GOAL_SCHEDULER', 'Fetched due goals', {
|
|
88
|
+
count: dueGoals.length,
|
|
89
|
+
});
|
|
90
|
+
this.emit('poll-completed', dueGoals.length);
|
|
91
|
+
const availableSlots = this.maxConcurrentExecutions - this.activeExecutions.size;
|
|
92
|
+
const goalsToExecute = dueGoals.slice(0, availableSlots);
|
|
93
|
+
for (const goal of goalsToExecute) {
|
|
94
|
+
if (this.activeExecutions.has(goal.hash)) {
|
|
95
|
+
this.logger.debug('GOAL_SCHEDULER', 'Skipping goal already running', {
|
|
96
|
+
goalHash: goal.hash,
|
|
97
|
+
goalName: goal.name,
|
|
98
|
+
});
|
|
99
|
+
this.emit('execution-skipped', goal, 'already_running');
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
this.executeGoalInBackground(goal);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
this.logger.error('GOAL_SCHEDULER', 'Poll failed', {
|
|
107
|
+
error: error instanceof Error ? error.message : String(error),
|
|
108
|
+
});
|
|
109
|
+
this.emit('error', error, { context: 'poll' });
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
this.isPolling = false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
executeGoalInBackground(goal) {
|
|
116
|
+
const goalHash = goal.hash;
|
|
117
|
+
const state = {
|
|
118
|
+
goalHash,
|
|
119
|
+
goalId: goal.id,
|
|
120
|
+
startedAt: new Date(),
|
|
121
|
+
status: 'running',
|
|
122
|
+
};
|
|
123
|
+
this.activeExecutions.set(goalHash, state);
|
|
124
|
+
this.totalExecutionCount++;
|
|
125
|
+
this.logger.info('GOAL_SCHEDULER', 'Starting goal execution', {
|
|
126
|
+
goalHash: goal.hash,
|
|
127
|
+
goalName: goal.name,
|
|
128
|
+
executionRecipe: goal.executionRecipe,
|
|
129
|
+
});
|
|
130
|
+
this.emit('execution-started', goal);
|
|
131
|
+
setImmediate(async () => {
|
|
132
|
+
try {
|
|
133
|
+
await this.markGoalStarted(goal.id, {
|
|
134
|
+
source: 'goal_scheduler',
|
|
135
|
+
startedAt: state.startedAt.toISOString(),
|
|
136
|
+
});
|
|
137
|
+
const result = await this.executeGoal(goal);
|
|
138
|
+
state.status = result.success ? 'completed' : 'failed';
|
|
139
|
+
await this.markGoalCompleted(goal.id, result.success ? 'success' : 'failed', {
|
|
140
|
+
error: result.error,
|
|
141
|
+
});
|
|
142
|
+
if (result.success) {
|
|
143
|
+
this.totalSuccessCount++;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
this.totalFailureCount++;
|
|
147
|
+
}
|
|
148
|
+
this.logger.info('GOAL_SCHEDULER', 'Goal execution completed', {
|
|
149
|
+
goalHash: goal.hash,
|
|
150
|
+
goalName: goal.name,
|
|
151
|
+
success: result.success,
|
|
152
|
+
error: result.error,
|
|
153
|
+
});
|
|
154
|
+
this.emit('execution-completed', goal, result.success, result.error);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
state.status = 'failed';
|
|
158
|
+
this.totalFailureCount++;
|
|
159
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
160
|
+
this.logger.error('GOAL_SCHEDULER', 'Goal execution failed', {
|
|
161
|
+
goalHash: goal.hash,
|
|
162
|
+
goalName: goal.name,
|
|
163
|
+
error: errorMessage,
|
|
164
|
+
});
|
|
165
|
+
try {
|
|
166
|
+
await this.markGoalCompleted(goal.id, 'failed', { error: errorMessage });
|
|
167
|
+
}
|
|
168
|
+
catch (reportError) {
|
|
169
|
+
this.logger.error('GOAL_SCHEDULER', 'Failed to report execution failure', {
|
|
170
|
+
goalHash: goal.hash,
|
|
171
|
+
error: reportError instanceof Error ? reportError.message : String(reportError),
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
this.emit('execution-completed', goal, false, errorMessage);
|
|
175
|
+
this.emit('error', error, {
|
|
176
|
+
context: 'execution',
|
|
177
|
+
goalHash: goal.hash,
|
|
178
|
+
goalName: goal.name,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
this.activeExecutions.delete(goalHash);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
getActiveExecutions() {
|
|
187
|
+
return Array.from(this.activeExecutions.values());
|
|
188
|
+
}
|
|
189
|
+
isGoalExecuting(goalHash) {
|
|
190
|
+
return this.activeExecutions.has(goalHash);
|
|
191
|
+
}
|
|
192
|
+
getStats() {
|
|
193
|
+
return {
|
|
194
|
+
running: this.started,
|
|
195
|
+
startedAt: this.startedAt,
|
|
196
|
+
activeExecutions: this.activeExecutions.size,
|
|
197
|
+
totalPollCount: this.totalPollCount,
|
|
198
|
+
totalExecutionCount: this.totalExecutionCount,
|
|
199
|
+
totalSuccessCount: this.totalSuccessCount,
|
|
200
|
+
totalFailureCount: this.totalFailureCount,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
async triggerPollNow() {
|
|
204
|
+
await this.poll();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
export function createGoalScheduler(options) {
|
|
208
|
+
return new GoalScheduler(options);
|
|
209
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { CreateGoalInput, UpdateGoalInput, CreateMilestoneInput, UpdateMilestoneInput, CreateTaskInput, UpdateTaskInput, CreateMemoryInput, UpdateMemoryInput, CreateMetaGoalInput, UpdateMetaGoalInput, TaskFeedbackInput } from './types.js';
|
|
2
|
+
export interface GoalValidationConstraints {
|
|
3
|
+
readonly maxNameLength: number;
|
|
4
|
+
readonly minNameLength: number;
|
|
5
|
+
readonly maxDescriptionLength: number;
|
|
6
|
+
readonly maxTitleLength: number;
|
|
7
|
+
readonly minTitleLength: number;
|
|
8
|
+
readonly maxMemoryContentLength: number;
|
|
9
|
+
readonly minMemoryContentLength: number;
|
|
10
|
+
readonly maxMetaGoalDescriptionLength: number;
|
|
11
|
+
readonly minMetaGoalDescriptionLength: number;
|
|
12
|
+
readonly maxMetaGoalPriority: number;
|
|
13
|
+
readonly minMetaGoalPriority: number;
|
|
14
|
+
readonly maxFeedbackCommentLength: number;
|
|
15
|
+
readonly maxDependencies: number;
|
|
16
|
+
readonly namePattern: RegExp;
|
|
17
|
+
}
|
|
18
|
+
export declare const DEFAULT_GOAL_VALIDATION_CONSTRAINTS: GoalValidationConstraints;
|
|
19
|
+
export interface ValidationResult {
|
|
20
|
+
readonly valid: boolean;
|
|
21
|
+
readonly errors: ValidationIssue[];
|
|
22
|
+
}
|
|
23
|
+
export interface ValidationIssue {
|
|
24
|
+
readonly field: string;
|
|
25
|
+
readonly message: string;
|
|
26
|
+
readonly value?: unknown;
|
|
27
|
+
}
|
|
28
|
+
export declare class GoalValidator {
|
|
29
|
+
private readonly constraints;
|
|
30
|
+
constructor(constraints?: Partial<GoalValidationConstraints>);
|
|
31
|
+
validateCreateGoalInput(input: CreateGoalInput): void;
|
|
32
|
+
validateCreateGoalInputSafe(input: CreateGoalInput): ValidationResult;
|
|
33
|
+
validateUpdateGoalInput(input: UpdateGoalInput): void;
|
|
34
|
+
validateUpdateGoalInputSafe(input: UpdateGoalInput): ValidationResult;
|
|
35
|
+
validateCreateMilestoneInput(input: CreateMilestoneInput): void;
|
|
36
|
+
validateCreateMilestoneInputSafe(input: CreateMilestoneInput): ValidationResult;
|
|
37
|
+
validateUpdateMilestoneInput(input: UpdateMilestoneInput): void;
|
|
38
|
+
validateUpdateMilestoneInputSafe(input: UpdateMilestoneInput): ValidationResult;
|
|
39
|
+
validateCreateTaskInput(input: CreateTaskInput): void;
|
|
40
|
+
validateCreateTaskInputSafe(input: CreateTaskInput): ValidationResult;
|
|
41
|
+
validateUpdateTaskInput(input: UpdateTaskInput): void;
|
|
42
|
+
validateUpdateTaskInputSafe(input: UpdateTaskInput): ValidationResult;
|
|
43
|
+
validateTaskFeedbackInput(input: TaskFeedbackInput): void;
|
|
44
|
+
validateTaskFeedbackInputSafe(input: TaskFeedbackInput): ValidationResult;
|
|
45
|
+
validateCreateMemoryInput(input: CreateMemoryInput): void;
|
|
46
|
+
validateCreateMemoryInputSafe(input: CreateMemoryInput): ValidationResult;
|
|
47
|
+
validateUpdateMemoryInput(input: UpdateMemoryInput): void;
|
|
48
|
+
validateUpdateMemoryInputSafe(input: UpdateMemoryInput): ValidationResult;
|
|
49
|
+
validateCreateMetaGoalInput(input: CreateMetaGoalInput): void;
|
|
50
|
+
validateCreateMetaGoalInputSafe(input: CreateMetaGoalInput): ValidationResult;
|
|
51
|
+
validateUpdateMetaGoalInput(input: UpdateMetaGoalInput): void;
|
|
52
|
+
validateUpdateMetaGoalInputSafe(input: UpdateMetaGoalInput): ValidationResult;
|
|
53
|
+
validateGoalHash(hash: string): void;
|
|
54
|
+
private validateName;
|
|
55
|
+
private validateTitle;
|
|
56
|
+
private validateDescription;
|
|
57
|
+
private validateMemoryContent;
|
|
58
|
+
private validateMetaGoalDescription;
|
|
59
|
+
private validateMetaGoalPriority;
|
|
60
|
+
private validateMetaGoalScope;
|
|
61
|
+
private validateDateString;
|
|
62
|
+
private validateDependencies;
|
|
63
|
+
private issuesToRecord;
|
|
64
|
+
}
|
|
65
|
+
export declare function createGoalValidator(constraints?: Partial<GoalValidationConstraints>): GoalValidator;
|
|
66
|
+
export declare function validateCreateGoalInput(input: CreateGoalInput): void;
|
|
67
|
+
export declare function validateUpdateGoalInput(input: UpdateGoalInput): void;
|
|
68
|
+
export declare function validateCreateTaskInput(input: CreateTaskInput): void;
|
|
69
|
+
export declare function validateCreateMilestoneInput(input: CreateMilestoneInput): void;
|
|
70
|
+
export declare function validateCreateMemoryInput(input: CreateMemoryInput): void;
|
|
71
|
+
export declare function validateCreateMetaGoalInput(input: CreateMetaGoalInput): void;
|
|
72
|
+
export declare function validateGoalHash(hash: string): void;
|