@runfusion/fusion 0.43.1 → 0.44.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/automation-store-EKXXEE2S.js +10 -0
- package/dist/automation-store-PLNG3X72.js +10 -0
- package/dist/automation-store-RDDYZFUJ.js +10 -0
- package/dist/bin.js +8097 -1796
- package/dist/chunk-2JI3F6MG.js +948 -0
- package/dist/chunk-4L6CLWJC.js +8345 -0
- package/dist/chunk-4VNS5WPM.js +42 -0
- package/dist/chunk-5RGECGQL.js +1139 -0
- package/dist/chunk-6JDLN7IL.js +409 -0
- package/dist/chunk-AICJATVW.js +948 -0
- package/dist/chunk-AWKNRUOR.js +1148 -0
- package/dist/chunk-BHM3UYWS.js +4651 -0
- package/dist/chunk-BMUXDSBF.js +4647 -0
- package/dist/chunk-GETECT6V.js +353 -0
- package/dist/chunk-GUV4QQT3.js +1143 -0
- package/dist/chunk-JKQO7YYZ.js +8369 -0
- package/dist/chunk-LYZVU6UV.js +409 -0
- package/dist/chunk-MAN4WYHO.js +353 -0
- package/dist/chunk-MCUG2Z7M.js +4651 -0
- package/dist/chunk-PSDYB45U.js +948 -0
- package/dist/chunk-RR2HM46R.js +8345 -0
- package/dist/chunk-RXMQ5OMY.js +9226 -0
- package/dist/chunk-S2EVJUY3.js +353 -0
- package/dist/chunk-UXR5XZFZ.js +409 -0
- package/dist/client/.vite/manifest.json +272 -261
- package/dist/client/assets/{AgentDetailView-BGN03dyz.css → AgentDetailView-CanIeYHg.css} +1 -1
- package/dist/client/assets/{AgentDetailView-DKMz3r04.js → AgentDetailView-DxV8pFHX.js} +3 -3
- package/dist/client/assets/{AgentPermissionPolicyEditor-BiY1a4KR.js → AgentPermissionPolicyEditor--YS4iMUY.js} +2 -2
- package/dist/client/assets/AgentsView-BVILQjdS.css +1 -0
- package/dist/client/assets/{AgentsView-Co-k4vjh.js → AgentsView-CTzbcvhK.js} +3 -3
- package/dist/client/assets/ChatView-COygcnyk.js +6 -0
- package/dist/client/assets/CommandCenter-CTaopaC0.css +1 -0
- package/dist/client/assets/CommandCenter-DoKX2HWm.js +56 -0
- package/dist/client/assets/DevServerView-Cyevyvqx.js +1 -0
- package/dist/client/assets/{DirectoryPicker-DTKtd8L2.css → DirectoryPicker-0rspvcMz.css} +1 -1
- package/dist/client/assets/{DirectoryPicker-CkcGjyv0.js → DirectoryPicker-DBdyXEhR.js} +1 -1
- package/dist/client/assets/DocumentsView-BzSMyrns.js +1 -0
- package/dist/client/assets/{EvalsView-CUfyj5GW.js → EvalsView-ikz00o9g.js} +1 -1
- package/dist/client/assets/ExperimentalAgentOnboardingModal-CZ_jbgep.css +1 -0
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-Dau9RTU_.js → ExperimentalAgentOnboardingModal-D-rzCCET.js} +10 -10
- package/dist/client/assets/GoalsView-Bfu1Oy2-.js +2 -0
- package/dist/client/assets/GoalsView-rg9VSIza.css +1 -0
- package/dist/client/assets/{InsightsView-BQPxHxKI.js → InsightsView-Cgmf5nES.js} +1 -1
- package/dist/client/assets/InsightsView-LMv1M1dU.css +1 -0
- package/dist/client/assets/MemoryView-DkEuDKQs.js +2 -0
- package/dist/client/assets/{NodesView-CSMvGwDU.js → NodesView-B_KYfS4o.js} +3 -3
- package/dist/client/assets/{PiExtensionsManager-BtqXF3fl.js → PiExtensionsManager-Dz69kkKs.js} +2 -2
- package/dist/client/assets/PluginManager-DSiHizRD.css +1 -0
- package/dist/client/assets/{PluginManager-CSSDb3VO.js → PluginManager-LHCVcduP.js} +1 -1
- package/dist/client/assets/PullRequestView-3AdbS2a1.css +1 -0
- package/dist/client/assets/{PullRequestView-Bjyfffdu.js → PullRequestView-DBJutLLl.js} +2 -2
- package/dist/client/assets/{ResearchView-CRU_NPho.js → ResearchView-gpn1XcVy.js} +1 -1
- package/dist/client/assets/{SecretsView-Bs8ySyC5.js → SecretsView-B7YqIowt.js} +1 -1
- package/dist/client/assets/SessionTerminal-9l4bdKcn.js +2 -0
- package/dist/client/assets/SessionTerminal-BNgtmXb3.css +1 -0
- package/dist/client/assets/SettingsModal-BGEK9_9A.js +31 -0
- package/dist/client/assets/{SettingsModal-DpAxDq6M.css → SettingsModal-CKv0TJfM.css} +1 -1
- package/dist/client/assets/SettingsModal-CXogw-Er.css +1 -0
- package/dist/client/assets/{SettingsModal-Nw5cTKAK.js → SettingsModal-DIIoHGUH.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-CE79myEy.js → SetupWizardModal-BcVeMxeX.js} +1 -1
- package/dist/client/assets/{SkillsView-Dc8YskAX.js → SkillsView-BxSJ4PHL.js} +1 -1
- package/dist/client/assets/{StashRecoveryView-CqKOFiXT.js → StashRecoveryView-BhFQ2FsP.js} +1 -1
- package/dist/client/assets/{TodoView-BqIRwcZw.js → TodoView-BZpewjy0.js} +2 -2
- package/dist/client/assets/WorkflowNodeEditor-Yn1FRF2v.css +1 -0
- package/dist/client/assets/WorkflowNodeEditor-tWsgCdVt.js +8 -0
- package/dist/client/assets/{addon-fit-B4jkcKk4.js → addon-fit-SnYDEvGm.js} +1 -1
- package/dist/client/assets/{addon-unicode11-Cdne2Sj4.js → addon-unicode11-s5GoZCpi.js} +1 -1
- package/dist/client/assets/{addon-web-links-CRNLzj7m.js → addon-web-links-jjt2f_oV.js} +1 -1
- package/dist/client/assets/{addon-webgl-CeCSULF1.js → addon-webgl-Icn-RnEx.js} +1 -1
- package/dist/client/assets/{app-sEkTyT-w.js → app-BmGA0Hiz.js} +3 -3
- package/dist/client/assets/{app-BffGU_HQ.js → app-CEVYlVuJ.js} +3 -3
- package/dist/client/assets/{app-C8AHhs5N.js → app-CbBFax2C.js} +3 -3
- package/dist/client/assets/{app-B2ZxMfJT.js → app-DORKrGAz.js} +3 -3
- package/dist/client/assets/{app-Ri9Te2Dv.js → app-DQibuQ9z.js} +3 -3
- package/dist/client/assets/app-k76GrQP0.js +13 -0
- package/dist/client/assets/{createLucideIcon-BYvOGRiO.js → createLucideIcon-DmVITl5d.js} +1 -1
- package/dist/client/assets/{dashboard-view-C3SsjXo1.js → dashboard-view-B4LXCv5H.js} +4 -4
- package/dist/client/assets/{dashboard-view-DlKsVySD.js → dashboard-view-BFoyhTYF.js} +1 -1
- package/dist/client/assets/{dashboard-view-BhbN8LPh.js → dashboard-view-Cxzm_eqc.js} +1 -1
- package/dist/client/assets/dashboard-view-_8yfNwju.css +1 -0
- package/dist/client/assets/{folder-open-Bw1mxVfa.js → folder-open-33WGVnbp.js} +1 -1
- package/dist/client/assets/index-Bglw05rM.js +791 -0
- package/dist/client/assets/index-FYgnflWD.css +1 -0
- package/dist/client/assets/shield-alert-CqeGWBUK.js +6 -0
- package/dist/client/assets/{upload-BCmd-1I0.js → upload-C11J2diI.js} +1 -1
- package/dist/client/assets/{users-DO_t3V1p.js → users-a-ELbyyZ.js} +1 -1
- package/dist/client/assets/{vendor-i18n-CV0mwX_-.js → vendor-i18n-DYlSUfot.js} +1 -1
- package/dist/client/assets/vendor-react-BTgM5bbZ.js +49 -0
- package/dist/client/assets/{vendor-reactflow-CRZcPCXF.js → vendor-reactflow-ZasuEfF9.js} +3 -3
- package/dist/client/assets/{vendor-xterm-tcedd2Jt.js → vendor-xterm-ea7-K05z.js} +1 -1
- package/dist/client/assets/{x-CqNX_9E2.js → x-PItWc-wQ.js} +1 -1
- package/dist/client/assets/{zoom-out-CSTHbJgD.js → zoom-out-CzlHv0Kj.js} +1 -1
- package/dist/client/icons/icon-192.png +0 -0
- package/dist/client/icons/icon-512.png +0 -0
- package/dist/client/index.html +6 -6
- package/dist/client/sw.js +1 -1
- package/dist/client/theme-data.css +288 -285
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/droid-cli/src/__tests__/execsync-guard.test.ts +17 -0
- package/dist/droid-cli/src/__tests__/process-manager.test.ts +1 -45
- package/dist/extension.js +4780 -793
- package/dist/extract-zip-WL55Z3AU.js +2497 -0
- package/dist/index.min-CHQ3ABYJ.js +2926 -0
- package/dist/memory-dreams-7USISKVE.js +32 -0
- package/dist/memory-dreams-PBPISUDD.js +32 -0
- package/dist/memory-dreams-XNO2B2QF.js +32 -0
- package/dist/migration-EH2YOCYQ.js +15 -0
- package/dist/migration-PLGTTKL5.js +15 -0
- package/dist/migration-WZEZK22R.js +15 -0
- package/dist/pi-claude-cli/index.ts +47 -8
- package/dist/pi-claude-cli/package.json +4 -1
- package/dist/pi-claude-cli/src/__tests__/acp-dispatch.test.ts +80 -0
- package/dist/pi-claude-cli/src/__tests__/acp-driver.test.ts +423 -0
- package/dist/pi-claude-cli/src/__tests__/execsync-guard.test.ts +14 -0
- package/dist/pi-claude-cli/src/__tests__/process-manager.test.ts +1 -45
- package/dist/pi-claude-cli/src/__tests__/provider.test.ts +0 -1
- package/dist/pi-claude-cli/src/acp-driver.ts +585 -0
- package/dist/pi-claude-cli/src/event-bridge.ts +7 -0
- package/dist/pi-claude-cli/src/mcp-config.ts +29 -0
- package/dist/pi-claude-cli/src/process-manager.ts +6 -38
- package/dist/plugin-sdk/index.d.ts +5179 -5144
- package/dist/plugins/fusion-plugin-acp-runtime/bundled.js +263 -19
- package/dist/plugins/fusion-plugin-acp-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +1 -1
- package/dist/plugins/fusion-plugin-compound-engineering/bundled.js +37 -7
- package/dist/plugins/fusion-plugin-compound-engineering/manifest.json +4 -4
- package/dist/plugins/fusion-plugin-compound-engineering/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/package.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/src/__tests__/index.test.ts +5 -1
- package/dist/routine-store-GIJ6MXWY.js +10 -0
- package/dist/routine-store-HTVC2PUQ.js +10 -0
- package/dist/routine-store-KGHZBPDF.js +10 -0
- package/package.json +1 -1
- package/skill/fusion/references/engine-tools.md +6 -4
- package/dist/client/assets/AgentsView-DfznJDek.css +0 -1
- package/dist/client/assets/ChatView-BkAG99bj.js +0 -6
- package/dist/client/assets/DevServerView-C6DC0URX.js +0 -1
- package/dist/client/assets/DocumentsView-OCK1Vsw_.js +0 -1
- package/dist/client/assets/ExperimentalAgentOnboardingModal-BNpveCEk.css +0 -1
- package/dist/client/assets/GoalsView-CNLTn0rX.js +0 -2
- package/dist/client/assets/GoalsView-DI66UsjT.css +0 -1
- package/dist/client/assets/InsightsView-C9-mqVvf.css +0 -1
- package/dist/client/assets/MemoryView-D3xQIFjj.js +0 -2
- package/dist/client/assets/PluginManager-UHVLvWWl.css +0 -1
- package/dist/client/assets/PullRequestView-WQIS0zUC.css +0 -1
- package/dist/client/assets/ReliabilityView-QKMUDebT.js +0 -1
- package/dist/client/assets/ReliabilityView-eoKBhBc4.css +0 -1
- package/dist/client/assets/SessionTerminal-BiigPP2l.css +0 -1
- package/dist/client/assets/SessionTerminal-Dk1NgmN2.js +0 -2
- package/dist/client/assets/SettingsModal-JCaNvdAb.js +0 -31
- package/dist/client/assets/SettingsModal-nF_GaeeK.css +0 -1
- package/dist/client/assets/WorkflowNodeEditor-2jDuk2FR.js +0 -8
- package/dist/client/assets/WorkflowNodeEditor-BokhW3de.css +0 -1
- package/dist/client/assets/app-CUu1sm3x.js +0 -13
- package/dist/client/assets/dashboard-view-Du53JbmQ.css +0 -1
- package/dist/client/assets/index-CpTBp1RG.js +0 -793
- package/dist/client/assets/index-DqhCiU35.css +0 -1
- package/dist/client/assets/vendor-react-D37HfzIf.js +0 -49
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertProjectRootDir,
|
|
3
|
+
require_dist
|
|
4
|
+
} from "./chunk-RXMQ5OMY.js";
|
|
5
|
+
import {
|
|
6
|
+
Database,
|
|
7
|
+
fromJson
|
|
8
|
+
} from "./chunk-BHM3UYWS.js";
|
|
9
|
+
import {
|
|
10
|
+
__toESM
|
|
11
|
+
} from "./chunk-4VNS5WPM.js";
|
|
12
|
+
|
|
13
|
+
// ../core/dist/automation-store.js
|
|
14
|
+
var import_cron_parser = __toESM(require_dist(), 1);
|
|
15
|
+
import { EventEmitter } from "events";
|
|
16
|
+
import { join } from "path";
|
|
17
|
+
import { randomUUID } from "crypto";
|
|
18
|
+
|
|
19
|
+
// ../core/dist/automation.js
|
|
20
|
+
var AUTOMATION_PRESETS = {
|
|
21
|
+
hourly: "0 * * * *",
|
|
22
|
+
daily: "0 0 * * *",
|
|
23
|
+
weekly: "0 0 * * 1",
|
|
24
|
+
monthly: "0 0 1 * *",
|
|
25
|
+
every15Minutes: "*/15 * * * *",
|
|
26
|
+
every30Minutes: "*/30 * * * *",
|
|
27
|
+
every2Hours: "0 */2 * * *",
|
|
28
|
+
every6Hours: "0 */6 * * *",
|
|
29
|
+
every12Hours: "0 */12 * * *",
|
|
30
|
+
weekdays: "0 9 * * 1-5"
|
|
31
|
+
};
|
|
32
|
+
var MAX_RUN_HISTORY = 50;
|
|
33
|
+
|
|
34
|
+
// ../core/dist/automation-store.js
|
|
35
|
+
var CRON_TIMEZONE = "UTC";
|
|
36
|
+
var AutomationStore = class _AutomationStore extends EventEmitter {
|
|
37
|
+
rootDir;
|
|
38
|
+
/** Per-schedule promise chain for serializing writes. */
|
|
39
|
+
scheduleLocks = /* @__PURE__ */ new Map();
|
|
40
|
+
/** SQLite database instance */
|
|
41
|
+
_db = null;
|
|
42
|
+
inMemoryDb;
|
|
43
|
+
constructor(rootDir, options) {
|
|
44
|
+
super();
|
|
45
|
+
this.rootDir = rootDir;
|
|
46
|
+
assertProjectRootDir(rootDir, "AutomationStore");
|
|
47
|
+
this.inMemoryDb = options?.inMemoryDb === true;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get the SQLite database, initializing it on first access.
|
|
51
|
+
*/
|
|
52
|
+
get db() {
|
|
53
|
+
if (!this._db) {
|
|
54
|
+
const fusionDir = join(this.rootDir, ".fusion");
|
|
55
|
+
this._db = new Database(fusionDir, { inMemory: this.inMemoryDb });
|
|
56
|
+
this._db.init();
|
|
57
|
+
}
|
|
58
|
+
return this._db;
|
|
59
|
+
}
|
|
60
|
+
/** Initialize the store. */
|
|
61
|
+
async init() {
|
|
62
|
+
const _ = this.db;
|
|
63
|
+
}
|
|
64
|
+
// ── Row Conversion ─────────────────────────────────────────────────
|
|
65
|
+
rowToSchedule(row) {
|
|
66
|
+
return {
|
|
67
|
+
id: row.id,
|
|
68
|
+
name: row.name,
|
|
69
|
+
description: row.description || void 0,
|
|
70
|
+
scheduleType: row.scheduleType,
|
|
71
|
+
cronExpression: row.cronExpression,
|
|
72
|
+
command: row.command,
|
|
73
|
+
enabled: row.enabled === 1,
|
|
74
|
+
timeoutMs: row.timeoutMs ?? void 0,
|
|
75
|
+
steps: fromJson(row.steps),
|
|
76
|
+
nextRunAt: row.nextRunAt || void 0,
|
|
77
|
+
lastRunAt: row.lastRunAt || void 0,
|
|
78
|
+
lastRunResult: fromJson(row.lastRunResult),
|
|
79
|
+
runCount: row.runCount || 0,
|
|
80
|
+
runHistory: fromJson(row.runHistory) || [],
|
|
81
|
+
scope: row.scope || "project",
|
|
82
|
+
createdAt: row.createdAt,
|
|
83
|
+
updatedAt: row.updatedAt
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
upsertSchedule(schedule) {
|
|
87
|
+
this.db.prepare(`
|
|
88
|
+
INSERT OR REPLACE INTO automations (
|
|
89
|
+
id, name, description, scheduleType, cronExpression, command,
|
|
90
|
+
enabled, timeoutMs, steps, nextRunAt, lastRunAt, lastRunResult,
|
|
91
|
+
runCount, runHistory, scope, createdAt, updatedAt
|
|
92
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
93
|
+
`).run(schedule.id, schedule.name, schedule.description ?? null, schedule.scheduleType, schedule.cronExpression, schedule.command, schedule.enabled ? 1 : 0, schedule.timeoutMs ?? null, schedule.steps ? JSON.stringify(schedule.steps) : null, schedule.nextRunAt ?? null, schedule.lastRunAt ?? null, schedule.lastRunResult ? JSON.stringify(schedule.lastRunResult) : null, schedule.runCount || 0, JSON.stringify(schedule.runHistory || []), schedule.scope ?? "project", schedule.createdAt, schedule.updatedAt);
|
|
94
|
+
}
|
|
95
|
+
// ── Locking ────────────────────────────────────────────────────────
|
|
96
|
+
/**
|
|
97
|
+
* Serialize all mutations to a given schedule by chaining promises.
|
|
98
|
+
* Concurrent callers for the same ID will queue behind each other.
|
|
99
|
+
*/
|
|
100
|
+
withScheduleLock(id, fn) {
|
|
101
|
+
const prev = this.scheduleLocks.get(id) ?? Promise.resolve();
|
|
102
|
+
let resolve;
|
|
103
|
+
const next = new Promise((r) => {
|
|
104
|
+
resolve = r;
|
|
105
|
+
});
|
|
106
|
+
this.scheduleLocks.set(id, next);
|
|
107
|
+
return prev.then(async () => {
|
|
108
|
+
try {
|
|
109
|
+
return await fn();
|
|
110
|
+
} finally {
|
|
111
|
+
if (this.scheduleLocks.get(id) === next) {
|
|
112
|
+
this.scheduleLocks.delete(id);
|
|
113
|
+
}
|
|
114
|
+
resolve();
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
// ── Persistence ────────────────────────────────────────────────────
|
|
119
|
+
async readScheduleJson(id) {
|
|
120
|
+
const row = this.db.prepare("SELECT * FROM automations WHERE id = ?").get(id);
|
|
121
|
+
if (!row) {
|
|
122
|
+
throw Object.assign(new Error(`Schedule '${id}' not found`), { code: "ENOENT" });
|
|
123
|
+
}
|
|
124
|
+
return this.rowToSchedule(row);
|
|
125
|
+
}
|
|
126
|
+
async persistSchedule(schedule) {
|
|
127
|
+
this.upsertSchedule(schedule);
|
|
128
|
+
this.db.bumpLastModified();
|
|
129
|
+
}
|
|
130
|
+
// ── Cron Computation ───────────────────────────────────────────────
|
|
131
|
+
/**
|
|
132
|
+
* Compute the next run time from a cron expression.
|
|
133
|
+
* @param cronExpression - A valid cron expression (5 fields).
|
|
134
|
+
* @param fromDate - The date to compute from. Defaults to now.
|
|
135
|
+
* @returns ISO-8601 timestamp of the next run.
|
|
136
|
+
*/
|
|
137
|
+
computeNextRun(cronExpression, fromDate) {
|
|
138
|
+
const interval = import_cron_parser.CronExpressionParser.parse(cronExpression, {
|
|
139
|
+
currentDate: fromDate ?? /* @__PURE__ */ new Date(),
|
|
140
|
+
tz: CRON_TIMEZONE
|
|
141
|
+
});
|
|
142
|
+
const next = interval.next();
|
|
143
|
+
return next.toISOString() ?? new Date(next.getTime()).toISOString();
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Validate a cron expression. Returns true if valid.
|
|
147
|
+
*/
|
|
148
|
+
static isValidCron(cronExpression) {
|
|
149
|
+
try {
|
|
150
|
+
import_cron_parser.CronExpressionParser.parse(cronExpression);
|
|
151
|
+
return true;
|
|
152
|
+
} catch {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// ── CRUD ───────────────────────────────────────────────────────────
|
|
157
|
+
async createSchedule(input) {
|
|
158
|
+
if (!input.name?.trim()) {
|
|
159
|
+
throw new Error("Name is required and cannot be empty");
|
|
160
|
+
}
|
|
161
|
+
const hasSteps = input.steps && input.steps.length > 0;
|
|
162
|
+
if (!hasSteps && !input.command?.trim()) {
|
|
163
|
+
throw new Error("Command is required and cannot be empty");
|
|
164
|
+
}
|
|
165
|
+
let cronExpression;
|
|
166
|
+
if (input.scheduleType === "custom") {
|
|
167
|
+
if (!input.cronExpression?.trim()) {
|
|
168
|
+
throw new Error("Cron expression is required for custom schedule type");
|
|
169
|
+
}
|
|
170
|
+
if (!_AutomationStore.isValidCron(input.cronExpression)) {
|
|
171
|
+
throw new Error(`Invalid cron expression: "${input.cronExpression}"`);
|
|
172
|
+
}
|
|
173
|
+
cronExpression = input.cronExpression.trim();
|
|
174
|
+
} else {
|
|
175
|
+
cronExpression = AUTOMATION_PRESETS[input.scheduleType];
|
|
176
|
+
}
|
|
177
|
+
const id = randomUUID();
|
|
178
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
179
|
+
const enabled = input.enabled !== void 0 ? input.enabled : true;
|
|
180
|
+
const schedule = {
|
|
181
|
+
id,
|
|
182
|
+
name: input.name.trim(),
|
|
183
|
+
description: input.description?.trim() || void 0,
|
|
184
|
+
scheduleType: input.scheduleType,
|
|
185
|
+
cronExpression,
|
|
186
|
+
command: (input.command ?? "").trim(),
|
|
187
|
+
enabled,
|
|
188
|
+
runCount: 0,
|
|
189
|
+
runHistory: [],
|
|
190
|
+
timeoutMs: input.timeoutMs,
|
|
191
|
+
steps: hasSteps ? input.steps : void 0,
|
|
192
|
+
nextRunAt: enabled ? this.computeNextRun(cronExpression) : void 0,
|
|
193
|
+
scope: input.scope ?? "project",
|
|
194
|
+
createdAt: now,
|
|
195
|
+
updatedAt: now
|
|
196
|
+
};
|
|
197
|
+
await this.persistSchedule(schedule);
|
|
198
|
+
this.emit("schedule:created", schedule);
|
|
199
|
+
return schedule;
|
|
200
|
+
}
|
|
201
|
+
async getSchedule(id) {
|
|
202
|
+
return this.readScheduleJson(id);
|
|
203
|
+
}
|
|
204
|
+
async listSchedules() {
|
|
205
|
+
const rows = this.db.prepare("SELECT * FROM automations ORDER BY createdAt ASC").all();
|
|
206
|
+
return rows.map((row) => this.rowToSchedule(row));
|
|
207
|
+
}
|
|
208
|
+
async updateSchedule(id, updates) {
|
|
209
|
+
return this.withScheduleLock(id, async () => {
|
|
210
|
+
const schedule = await this.getSchedule(id);
|
|
211
|
+
const previousEnabled = schedule.enabled;
|
|
212
|
+
const previousScheduleType = schedule.scheduleType;
|
|
213
|
+
const previousCronExpression = schedule.cronExpression;
|
|
214
|
+
if (updates.name !== void 0) {
|
|
215
|
+
if (!updates.name.trim())
|
|
216
|
+
throw new Error("Name cannot be empty");
|
|
217
|
+
schedule.name = updates.name.trim();
|
|
218
|
+
}
|
|
219
|
+
if (updates.description !== void 0) {
|
|
220
|
+
schedule.description = updates.description?.trim() || void 0;
|
|
221
|
+
}
|
|
222
|
+
if (updates.command !== void 0) {
|
|
223
|
+
schedule.command = updates.command.trim();
|
|
224
|
+
}
|
|
225
|
+
if (updates.steps !== void 0) {
|
|
226
|
+
schedule.steps = updates.steps.length > 0 ? updates.steps : void 0;
|
|
227
|
+
}
|
|
228
|
+
const willHaveSteps = schedule.steps && schedule.steps.length > 0;
|
|
229
|
+
if (!willHaveSteps && !schedule.command) {
|
|
230
|
+
throw new Error("Command is required and cannot be empty");
|
|
231
|
+
}
|
|
232
|
+
if (updates.timeoutMs !== void 0) {
|
|
233
|
+
schedule.timeoutMs = updates.timeoutMs;
|
|
234
|
+
}
|
|
235
|
+
if (updates.scheduleType !== void 0 || updates.cronExpression !== void 0) {
|
|
236
|
+
const newType = updates.scheduleType ?? schedule.scheduleType;
|
|
237
|
+
let newCron;
|
|
238
|
+
if (newType === "custom") {
|
|
239
|
+
const customCron = updates.cronExpression ?? schedule.cronExpression;
|
|
240
|
+
if (!customCron?.trim()) {
|
|
241
|
+
throw new Error("Cron expression is required for custom schedule type");
|
|
242
|
+
}
|
|
243
|
+
if (!_AutomationStore.isValidCron(customCron)) {
|
|
244
|
+
throw new Error(`Invalid cron expression: "${customCron}"`);
|
|
245
|
+
}
|
|
246
|
+
newCron = customCron.trim();
|
|
247
|
+
} else {
|
|
248
|
+
newCron = AUTOMATION_PRESETS[newType];
|
|
249
|
+
}
|
|
250
|
+
schedule.scheduleType = newType;
|
|
251
|
+
schedule.cronExpression = newCron;
|
|
252
|
+
}
|
|
253
|
+
if (updates.enabled !== void 0) {
|
|
254
|
+
schedule.enabled = updates.enabled;
|
|
255
|
+
}
|
|
256
|
+
const cadenceChanged = schedule.scheduleType !== previousScheduleType || schedule.cronExpression !== previousCronExpression;
|
|
257
|
+
const enabledFromDisabled = !previousEnabled && schedule.enabled;
|
|
258
|
+
const missingNextRunAt = !schedule.nextRunAt;
|
|
259
|
+
if (!schedule.enabled) {
|
|
260
|
+
schedule.nextRunAt = void 0;
|
|
261
|
+
} else if (cadenceChanged || enabledFromDisabled || missingNextRunAt) {
|
|
262
|
+
schedule.nextRunAt = this.computeNextRun(schedule.cronExpression);
|
|
263
|
+
}
|
|
264
|
+
schedule.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
265
|
+
await this.persistSchedule(schedule);
|
|
266
|
+
this.emit("schedule:updated", schedule);
|
|
267
|
+
return schedule;
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Reorder the steps of a schedule by providing the step IDs in the desired order.
|
|
272
|
+
* The `stepIds` array must contain exactly the same IDs as the current steps.
|
|
273
|
+
*/
|
|
274
|
+
async reorderSteps(scheduleId, stepIds) {
|
|
275
|
+
return this.withScheduleLock(scheduleId, async () => {
|
|
276
|
+
const schedule = await this.getSchedule(scheduleId);
|
|
277
|
+
if (!schedule.steps || schedule.steps.length === 0) {
|
|
278
|
+
throw new Error("Schedule has no steps to reorder");
|
|
279
|
+
}
|
|
280
|
+
if (stepIds.length !== schedule.steps.length) {
|
|
281
|
+
throw new Error(`Step ID count mismatch: expected ${schedule.steps.length}, got ${stepIds.length}`);
|
|
282
|
+
}
|
|
283
|
+
const stepMap = new Map(schedule.steps.map((s) => [s.id, s]));
|
|
284
|
+
const reordered = [];
|
|
285
|
+
for (const id of stepIds) {
|
|
286
|
+
const step = stepMap.get(id);
|
|
287
|
+
if (!step) {
|
|
288
|
+
throw new Error(`Unknown step ID: "${id}"`);
|
|
289
|
+
}
|
|
290
|
+
reordered.push(step);
|
|
291
|
+
}
|
|
292
|
+
schedule.steps = reordered;
|
|
293
|
+
schedule.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
294
|
+
await this.persistSchedule(schedule);
|
|
295
|
+
this.emit("schedule:updated", schedule);
|
|
296
|
+
return schedule;
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
async deleteSchedule(id) {
|
|
300
|
+
return this.withScheduleLock(id, async () => {
|
|
301
|
+
const schedule = await this.getSchedule(id);
|
|
302
|
+
this.db.prepare("DELETE FROM automations WHERE id = ?").run(id);
|
|
303
|
+
this.db.bumpLastModified();
|
|
304
|
+
this.emit("schedule:deleted", schedule);
|
|
305
|
+
return schedule;
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Record a run result for a schedule. Updates lastRunAt, lastRunResult,
|
|
310
|
+
* nextRunAt, runCount, and appends to runHistory.
|
|
311
|
+
*/
|
|
312
|
+
async recordRun(id, result) {
|
|
313
|
+
return this.withScheduleLock(id, async () => {
|
|
314
|
+
const schedule = await this.getSchedule(id);
|
|
315
|
+
schedule.lastRunAt = result.startedAt;
|
|
316
|
+
schedule.lastRunResult = result;
|
|
317
|
+
schedule.runCount += 1;
|
|
318
|
+
schedule.runHistory.unshift(result);
|
|
319
|
+
if (schedule.runHistory.length > MAX_RUN_HISTORY) {
|
|
320
|
+
schedule.runHistory = schedule.runHistory.slice(0, MAX_RUN_HISTORY);
|
|
321
|
+
}
|
|
322
|
+
if (schedule.enabled) {
|
|
323
|
+
schedule.nextRunAt = this.computeNextRun(schedule.cronExpression);
|
|
324
|
+
}
|
|
325
|
+
schedule.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
326
|
+
await this.persistSchedule(schedule);
|
|
327
|
+
this.emit("schedule:run", { schedule, result });
|
|
328
|
+
return schedule;
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Get all schedules that are due to run (nextRunAt <= now and enabled).
|
|
333
|
+
* Filters by scope: "global" or "project".
|
|
334
|
+
*/
|
|
335
|
+
async getDueSchedules(scope) {
|
|
336
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
337
|
+
const rows = this.db.prepare("SELECT * FROM automations WHERE enabled = 1 AND nextRunAt IS NOT NULL AND nextRunAt <= ? AND scope = ?").all(now, scope);
|
|
338
|
+
return rows.map((row) => this.rowToSchedule(row));
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Get all schedules that are due to run (nextRunAt <= now and enabled) for both scopes.
|
|
342
|
+
* Returns schedules from both "global" and "project" scopes.
|
|
343
|
+
*/
|
|
344
|
+
async getDueSchedulesAllScopes() {
|
|
345
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
346
|
+
const rows = this.db.prepare("SELECT * FROM automations WHERE enabled = 1 AND nextRunAt IS NOT NULL AND nextRunAt <= ?").all(now);
|
|
347
|
+
return rows.map((row) => this.rowToSchedule(row));
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
export {
|
|
352
|
+
AutomationStore
|
|
353
|
+
};
|