feat(FN-1715): add scope-aware automation and routine scheduling

- Add scope field to automations and routines for granular control
- Update automation-store and routine-store with scope-aware query methods
- Extend database schema with scope column for automations and routines
- Update cron-runner and routine-scheduler to respect scope boundaries
- Add project context injection to in-process runtime for scoped execution
- Include changeset for minor version bump
This commit is contained in:
Fusion
2026-04-15 16:32:16 -07:00
committed by gsxdsm
parent a5188bb5eb
commit 08a0d7157a
18 changed files with 208 additions and 49 deletions

View File

@@ -119,6 +119,7 @@ export class RoutineStore extends EventEmitter<RoutineStoreEvents> {
runHistory: fromJson<RoutineExecutionResult[]>(row.runHistory) || [],
catchUpLimit: row.catchUpLimit ?? 5,
cronExpression: isCronTrigger(trigger) ? trigger.cronExpression : undefined,
scope: (row.scope as "global" | "project") || "project",
createdAt: row.createdAt,
updatedAt: row.updatedAt,
};
@@ -149,8 +150,8 @@ export class RoutineStore extends EventEmitter<RoutineStoreEvents> {
id, agentId, name, description, triggerType, triggerConfig,
catchUpPolicy, executionPolicy, catchUpLimit, enabled,
lastRunAt, lastRunResult, nextRunAt,
runCount, runHistory, createdAt, updatedAt
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
runCount, runHistory, scope, createdAt, updatedAt
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(
routine.id,
routine.agentId,
@@ -167,6 +168,7 @@ export class RoutineStore extends EventEmitter<RoutineStoreEvents> {
routine.nextRunAt ?? null,
routine.runCount || 0,
JSON.stringify(routine.runHistory || []),
routine.scope ?? "project",
routine.createdAt,
routine.updatedAt,
);
@@ -257,6 +259,7 @@ export class RoutineStore extends EventEmitter<RoutineStoreEvents> {
enabled,
runCount: 0,
runHistory: [],
scope: input.scope ?? "project",
createdAt: now,
updatedAt: now,
};
@@ -450,8 +453,21 @@ export class RoutineStore extends EventEmitter<RoutineStoreEvents> {
/**
* Get all routines that are due to run (nextRunAt <= now and enabled).
* Filters by scope: "global" or "project".
*/
async getDueRoutines(): Promise<Routine[]> {
async getDueRoutines(scope: "global" | "project"): Promise<Routine[]> {
const now = new Date().toISOString();
const rows = this.db.prepare(
"SELECT * FROM routines WHERE enabled = 1 AND nextRunAt IS NOT NULL AND nextRunAt <= ? AND scope = ?"
).all(now, scope) as any[];
return rows.map((row) => this.rowToRoutine(row));
}
/**
* Get all routines that are due to run (nextRunAt <= now and enabled) for both scopes.
* Returns routines from both "global" and "project" scopes.
*/
async getDueRoutinesAllScopes(): Promise<Routine[]> {
const now = new Date().toISOString();
const rows = this.db.prepare(
"SELECT * FROM routines WHERE enabled = 1 AND nextRunAt IS NOT NULL AND nextRunAt <= ?"