@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,409 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertProjectRootDir,
|
|
3
|
+
require_dist
|
|
4
|
+
} from "./chunk-RXMQ5OMY.js";
|
|
5
|
+
import {
|
|
6
|
+
Database,
|
|
7
|
+
fromJson
|
|
8
|
+
} from "./chunk-MCUG2Z7M.js";
|
|
9
|
+
import {
|
|
10
|
+
__toESM
|
|
11
|
+
} from "./chunk-4VNS5WPM.js";
|
|
12
|
+
|
|
13
|
+
// ../core/dist/routine-store.js
|
|
14
|
+
var import_cron_parser = __toESM(require_dist(), 1);
|
|
15
|
+
import { EventEmitter } from "events";
|
|
16
|
+
import { randomUUID } from "crypto";
|
|
17
|
+
|
|
18
|
+
// ../core/dist/routine.js
|
|
19
|
+
function isCronTrigger(trigger) {
|
|
20
|
+
return trigger.type === "cron";
|
|
21
|
+
}
|
|
22
|
+
var MAX_ROUTINE_RUN_HISTORY = 50;
|
|
23
|
+
|
|
24
|
+
// ../core/dist/routine-store.js
|
|
25
|
+
var CRON_TIMEZONE = "UTC";
|
|
26
|
+
var RoutineStore = class _RoutineStore extends EventEmitter {
|
|
27
|
+
rootDir;
|
|
28
|
+
/** SQLite database instance (lazy init). */
|
|
29
|
+
_db = null;
|
|
30
|
+
/** Per-routine promise chain for serializing writes. */
|
|
31
|
+
routineLocks = /* @__PURE__ */ new Map();
|
|
32
|
+
inMemoryDb;
|
|
33
|
+
constructor(rootDir, options) {
|
|
34
|
+
super();
|
|
35
|
+
this.rootDir = rootDir;
|
|
36
|
+
assertProjectRootDir(rootDir, "RoutineStore");
|
|
37
|
+
this.inMemoryDb = options?.inMemoryDb === true;
|
|
38
|
+
}
|
|
39
|
+
// ── Database Access ────────────────────────────────────────────────
|
|
40
|
+
/**
|
|
41
|
+
* Get the SQLite database, initializing it on first access.
|
|
42
|
+
*/
|
|
43
|
+
get db() {
|
|
44
|
+
if (!this._db) {
|
|
45
|
+
const fusionDir = `${this.rootDir}/.fusion`;
|
|
46
|
+
this._db = new Database(fusionDir, { inMemory: this.inMemoryDb });
|
|
47
|
+
this._db.init();
|
|
48
|
+
}
|
|
49
|
+
return this._db;
|
|
50
|
+
}
|
|
51
|
+
/** Initialize the store (no-op, DB is lazily initialized). */
|
|
52
|
+
async init() {
|
|
53
|
+
const _ = this.db;
|
|
54
|
+
}
|
|
55
|
+
// ── Row Conversion ─────────────────────────────────────────────────
|
|
56
|
+
rowToRoutine(row) {
|
|
57
|
+
const triggerConfig = fromJson(row.triggerConfig);
|
|
58
|
+
let trigger;
|
|
59
|
+
switch (row.triggerType) {
|
|
60
|
+
case "cron":
|
|
61
|
+
trigger = {
|
|
62
|
+
type: "cron",
|
|
63
|
+
cronExpression: triggerConfig?.cronExpression ?? "0 * * * *",
|
|
64
|
+
timezone: triggerConfig?.timezone
|
|
65
|
+
};
|
|
66
|
+
break;
|
|
67
|
+
case "webhook":
|
|
68
|
+
trigger = {
|
|
69
|
+
type: "webhook",
|
|
70
|
+
webhookPath: triggerConfig?.webhookPath ?? "",
|
|
71
|
+
secret: triggerConfig?.secret
|
|
72
|
+
};
|
|
73
|
+
break;
|
|
74
|
+
case "api":
|
|
75
|
+
trigger = {
|
|
76
|
+
type: "api",
|
|
77
|
+
endpoint: triggerConfig?.endpoint ?? ""
|
|
78
|
+
};
|
|
79
|
+
break;
|
|
80
|
+
case "manual":
|
|
81
|
+
default:
|
|
82
|
+
trigger = { type: "manual" };
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
id: row.id,
|
|
87
|
+
agentId: row.agentId || "",
|
|
88
|
+
name: row.name,
|
|
89
|
+
description: row.description || void 0,
|
|
90
|
+
trigger,
|
|
91
|
+
command: row.command || void 0,
|
|
92
|
+
steps: fromJson(row.steps),
|
|
93
|
+
timeoutMs: row.timeoutMs ?? void 0,
|
|
94
|
+
catchUpPolicy: row.catchUpPolicy || "run_one",
|
|
95
|
+
executionPolicy: row.executionPolicy || "queue",
|
|
96
|
+
enabled: row.enabled === 1,
|
|
97
|
+
lastRunAt: row.lastRunAt || void 0,
|
|
98
|
+
lastRunResult: fromJson(row.lastRunResult),
|
|
99
|
+
nextRunAt: row.nextRunAt || void 0,
|
|
100
|
+
runCount: row.runCount || 0,
|
|
101
|
+
runHistory: fromJson(row.runHistory) || [],
|
|
102
|
+
catchUpLimit: row.catchUpLimit ?? 5,
|
|
103
|
+
cronExpression: isCronTrigger(trigger) ? trigger.cronExpression : void 0,
|
|
104
|
+
scope: row.scope || "project",
|
|
105
|
+
createdAt: row.createdAt,
|
|
106
|
+
updatedAt: row.updatedAt
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
upsertRoutine(routine) {
|
|
110
|
+
const trigger = routine.trigger;
|
|
111
|
+
let triggerConfig = {};
|
|
112
|
+
if (isCronTrigger(trigger)) {
|
|
113
|
+
triggerConfig = {
|
|
114
|
+
cronExpression: trigger.cronExpression,
|
|
115
|
+
timezone: trigger.timezone
|
|
116
|
+
};
|
|
117
|
+
} else if (trigger.type === "webhook") {
|
|
118
|
+
triggerConfig = {
|
|
119
|
+
webhookPath: trigger.webhookPath,
|
|
120
|
+
secret: trigger.secret
|
|
121
|
+
};
|
|
122
|
+
} else if (trigger.type === "api") {
|
|
123
|
+
triggerConfig = {
|
|
124
|
+
endpoint: trigger.endpoint
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
this.db.prepare(`
|
|
128
|
+
INSERT OR REPLACE INTO routines (
|
|
129
|
+
id, agentId, name, description, triggerType, triggerConfig,
|
|
130
|
+
command, steps, timeoutMs,
|
|
131
|
+
catchUpPolicy, executionPolicy, catchUpLimit, enabled,
|
|
132
|
+
lastRunAt, lastRunResult, nextRunAt,
|
|
133
|
+
runCount, runHistory, scope, createdAt, updatedAt
|
|
134
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
135
|
+
`).run(routine.id, routine.agentId, routine.name, routine.description ?? null, trigger.type, JSON.stringify(triggerConfig), routine.command ?? null, routine.steps ? JSON.stringify(routine.steps) : null, routine.timeoutMs ?? null, routine.catchUpPolicy, routine.executionPolicy, routine.catchUpLimit ?? 5, routine.enabled ? 1 : 0, routine.lastRunAt ?? null, routine.lastRunResult ? JSON.stringify(routine.lastRunResult) : null, routine.nextRunAt ?? null, routine.runCount || 0, JSON.stringify(routine.runHistory || []), routine.scope ?? "project", routine.createdAt, routine.updatedAt);
|
|
136
|
+
this.db.bumpLastModified();
|
|
137
|
+
}
|
|
138
|
+
// ── Locking ───────────────────────────────────────────────────────
|
|
139
|
+
/**
|
|
140
|
+
* Serialize all mutations to a given routine by chaining promises.
|
|
141
|
+
* Concurrent callers for the same ID will queue behind each other.
|
|
142
|
+
*/
|
|
143
|
+
withRoutineLock(id, fn) {
|
|
144
|
+
const prev = this.routineLocks.get(id) ?? Promise.resolve();
|
|
145
|
+
let resolve;
|
|
146
|
+
const next = new Promise((r) => {
|
|
147
|
+
resolve = r;
|
|
148
|
+
});
|
|
149
|
+
this.routineLocks.set(id, next);
|
|
150
|
+
return prev.then(async () => {
|
|
151
|
+
try {
|
|
152
|
+
return await fn();
|
|
153
|
+
} finally {
|
|
154
|
+
if (this.routineLocks.get(id) === next) {
|
|
155
|
+
this.routineLocks.delete(id);
|
|
156
|
+
}
|
|
157
|
+
resolve();
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
// ── Cron Utilities ────────────────────────────────────────────────
|
|
162
|
+
/**
|
|
163
|
+
* Compute the next run time from a cron expression.
|
|
164
|
+
* @param cronExpression - A valid cron expression (5 fields).
|
|
165
|
+
* @param fromDate - The date to compute from. Defaults to now.
|
|
166
|
+
* @returns ISO-8601 timestamp of the next run.
|
|
167
|
+
*/
|
|
168
|
+
computeNextRun(cronExpression, fromDate) {
|
|
169
|
+
const interval = import_cron_parser.CronExpressionParser.parse(cronExpression, {
|
|
170
|
+
currentDate: fromDate ?? /* @__PURE__ */ new Date(),
|
|
171
|
+
tz: CRON_TIMEZONE
|
|
172
|
+
});
|
|
173
|
+
const next = interval.next();
|
|
174
|
+
return new Date(next.getTime()).toISOString();
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Validate a cron expression. Returns true if valid.
|
|
178
|
+
*/
|
|
179
|
+
static isValidCron(cronExpression) {
|
|
180
|
+
try {
|
|
181
|
+
import_cron_parser.CronExpressionParser.parse(cronExpression);
|
|
182
|
+
return true;
|
|
183
|
+
} catch {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// ── CRUD ──────────────────────────────────────────────────────────
|
|
188
|
+
/**
|
|
189
|
+
* Create a new routine.
|
|
190
|
+
*/
|
|
191
|
+
async createRoutine(input) {
|
|
192
|
+
if (!input.name?.trim()) {
|
|
193
|
+
throw new Error("Name is required and cannot be empty");
|
|
194
|
+
}
|
|
195
|
+
if (isCronTrigger(input.trigger)) {
|
|
196
|
+
if (!_RoutineStore.isValidCron(input.trigger.cronExpression)) {
|
|
197
|
+
throw new Error(`Invalid cron expression: "${input.trigger.cronExpression}"`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const id = randomUUID();
|
|
201
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
202
|
+
const enabled = input.enabled !== void 0 ? input.enabled : true;
|
|
203
|
+
const routine = {
|
|
204
|
+
id,
|
|
205
|
+
agentId: input.agentId,
|
|
206
|
+
name: input.name.trim(),
|
|
207
|
+
description: input.description?.trim() || void 0,
|
|
208
|
+
trigger: input.trigger,
|
|
209
|
+
command: input.command?.trim() || void 0,
|
|
210
|
+
steps: input.steps && input.steps.length > 0 ? input.steps : void 0,
|
|
211
|
+
timeoutMs: input.timeoutMs,
|
|
212
|
+
catchUpPolicy: input.catchUpPolicy ?? "run_one",
|
|
213
|
+
executionPolicy: input.executionPolicy ?? "queue",
|
|
214
|
+
enabled,
|
|
215
|
+
runCount: 0,
|
|
216
|
+
runHistory: [],
|
|
217
|
+
scope: input.scope ?? "project",
|
|
218
|
+
createdAt: now,
|
|
219
|
+
updatedAt: now
|
|
220
|
+
};
|
|
221
|
+
if (enabled && isCronTrigger(routine.trigger)) {
|
|
222
|
+
routine.nextRunAt = this.computeNextRun(routine.trigger.cronExpression);
|
|
223
|
+
}
|
|
224
|
+
this.upsertRoutine(routine);
|
|
225
|
+
this.emit("routine:created", routine);
|
|
226
|
+
return routine;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get a routine by ID.
|
|
230
|
+
*/
|
|
231
|
+
async getRoutine(id) {
|
|
232
|
+
const row = this.db.prepare("SELECT * FROM routines WHERE id = ?").get(id);
|
|
233
|
+
if (!row) {
|
|
234
|
+
throw Object.assign(new Error(`Routine '${id}' not found`), { code: "ENOENT" });
|
|
235
|
+
}
|
|
236
|
+
return this.rowToRoutine(row);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* List all routines.
|
|
240
|
+
*/
|
|
241
|
+
async listRoutines() {
|
|
242
|
+
const rows = this.db.prepare("SELECT * FROM routines ORDER BY createdAt ASC").all();
|
|
243
|
+
return rows.map((row) => this.rowToRoutine(row));
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Update an existing routine.
|
|
247
|
+
*/
|
|
248
|
+
async updateRoutine(id, updates) {
|
|
249
|
+
return this.withRoutineLock(id, async () => {
|
|
250
|
+
const routine = await this.getRoutine(id);
|
|
251
|
+
if (updates.name !== void 0) {
|
|
252
|
+
if (!updates.name.trim())
|
|
253
|
+
throw new Error("Name cannot be empty");
|
|
254
|
+
routine.name = updates.name.trim();
|
|
255
|
+
}
|
|
256
|
+
if (updates.description !== void 0) {
|
|
257
|
+
routine.description = updates.description?.trim() || void 0;
|
|
258
|
+
}
|
|
259
|
+
if (updates.trigger !== void 0) {
|
|
260
|
+
if (isCronTrigger(updates.trigger)) {
|
|
261
|
+
if (!_RoutineStore.isValidCron(updates.trigger.cronExpression)) {
|
|
262
|
+
throw new Error(`Invalid cron expression: "${updates.trigger.cronExpression}"`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
routine.trigger = updates.trigger;
|
|
266
|
+
}
|
|
267
|
+
if (updates.command !== void 0) {
|
|
268
|
+
routine.command = updates.command?.trim() || void 0;
|
|
269
|
+
}
|
|
270
|
+
if (updates.steps !== void 0) {
|
|
271
|
+
routine.steps = updates.steps.length > 0 ? updates.steps : void 0;
|
|
272
|
+
}
|
|
273
|
+
if (updates.timeoutMs !== void 0) {
|
|
274
|
+
routine.timeoutMs = updates.timeoutMs;
|
|
275
|
+
}
|
|
276
|
+
if (updates.catchUpPolicy !== void 0) {
|
|
277
|
+
routine.catchUpPolicy = updates.catchUpPolicy;
|
|
278
|
+
}
|
|
279
|
+
if (updates.executionPolicy !== void 0) {
|
|
280
|
+
routine.executionPolicy = updates.executionPolicy;
|
|
281
|
+
}
|
|
282
|
+
if (updates.enabled !== void 0) {
|
|
283
|
+
routine.enabled = updates.enabled;
|
|
284
|
+
}
|
|
285
|
+
if (routine.enabled && isCronTrigger(routine.trigger)) {
|
|
286
|
+
routine.nextRunAt = this.computeNextRun(routine.trigger.cronExpression);
|
|
287
|
+
} else if (!routine.enabled || !isCronTrigger(routine.trigger)) {
|
|
288
|
+
routine.nextRunAt = void 0;
|
|
289
|
+
}
|
|
290
|
+
routine.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
291
|
+
this.upsertRoutine(routine);
|
|
292
|
+
this.emit("routine:updated", routine);
|
|
293
|
+
return routine;
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Delete a routine.
|
|
298
|
+
*/
|
|
299
|
+
async deleteRoutine(id) {
|
|
300
|
+
return this.withRoutineLock(id, async () => {
|
|
301
|
+
const routine = await this.getRoutine(id);
|
|
302
|
+
this.db.prepare("DELETE FROM routines WHERE id = ?").run(id);
|
|
303
|
+
this.db.bumpLastModified();
|
|
304
|
+
this.emit("routine:deleted", routine);
|
|
305
|
+
return routine;
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
// ── Run Tracking ─────────────────────────────────────────────────
|
|
309
|
+
/**
|
|
310
|
+
* Record a run result for a routine. Updates lastRunAt, lastRunResult,
|
|
311
|
+
* nextRunAt, runCount, and appends to runHistory.
|
|
312
|
+
*/
|
|
313
|
+
async recordRun(id, result) {
|
|
314
|
+
return this.withRoutineLock(id, async () => {
|
|
315
|
+
const routine = await this.getRoutine(id);
|
|
316
|
+
routine.lastRunAt = result.startedAt;
|
|
317
|
+
routine.lastRunResult = result;
|
|
318
|
+
routine.runCount += 1;
|
|
319
|
+
routine.runHistory.unshift(result);
|
|
320
|
+
if (routine.runHistory.length > MAX_ROUTINE_RUN_HISTORY) {
|
|
321
|
+
routine.runHistory = routine.runHistory.slice(0, MAX_ROUTINE_RUN_HISTORY);
|
|
322
|
+
}
|
|
323
|
+
if (routine.enabled && isCronTrigger(routine.trigger)) {
|
|
324
|
+
routine.nextRunAt = this.computeNextRun(routine.trigger.cronExpression);
|
|
325
|
+
}
|
|
326
|
+
routine.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
327
|
+
this.upsertRoutine(routine);
|
|
328
|
+
this.emit("routine:run", { routine, result });
|
|
329
|
+
return routine;
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Mark a routine execution as started (pre-run bookkeeping).
|
|
334
|
+
*/
|
|
335
|
+
async startRoutineExecution(id, meta) {
|
|
336
|
+
await this.withRoutineLock(id, async () => {
|
|
337
|
+
const routine = await this.getRoutine(id);
|
|
338
|
+
routine.lastRunAt = meta.triggeredAt;
|
|
339
|
+
routine.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
340
|
+
this.upsertRoutine(routine);
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Record the completion (success or failure) of a routine execution.
|
|
345
|
+
*/
|
|
346
|
+
async completeRoutineExecution(id, meta) {
|
|
347
|
+
await this.withRoutineLock(id, async () => {
|
|
348
|
+
const routine = await this.getRoutine(id);
|
|
349
|
+
const result = {
|
|
350
|
+
routineId: id,
|
|
351
|
+
success: meta.success,
|
|
352
|
+
output: meta.output ?? (meta.success ? JSON.stringify(meta.resultJson ?? {}) : ""),
|
|
353
|
+
error: meta.error,
|
|
354
|
+
startedAt: routine.lastRunAt ?? meta.completedAt,
|
|
355
|
+
completedAt: meta.completedAt,
|
|
356
|
+
triggerType: meta.triggerType,
|
|
357
|
+
stepResults: meta.stepResults
|
|
358
|
+
};
|
|
359
|
+
routine.lastRunAt = result.startedAt;
|
|
360
|
+
routine.lastRunResult = result;
|
|
361
|
+
routine.runCount += 1;
|
|
362
|
+
routine.runHistory.unshift(result);
|
|
363
|
+
if (routine.runHistory.length > MAX_ROUTINE_RUN_HISTORY) {
|
|
364
|
+
routine.runHistory = routine.runHistory.slice(0, MAX_ROUTINE_RUN_HISTORY);
|
|
365
|
+
}
|
|
366
|
+
if (routine.enabled && isCronTrigger(routine.trigger)) {
|
|
367
|
+
routine.nextRunAt = this.computeNextRun(routine.trigger.cronExpression);
|
|
368
|
+
}
|
|
369
|
+
routine.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
370
|
+
this.upsertRoutine(routine);
|
|
371
|
+
this.emit("routine:run", { routine, result });
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Cancel a routine execution (no result recorded, just reset state).
|
|
376
|
+
*/
|
|
377
|
+
async cancelRoutineExecution(id) {
|
|
378
|
+
await this.withRoutineLock(id, async () => {
|
|
379
|
+
const routine = await this.getRoutine(id);
|
|
380
|
+
if (routine.enabled && isCronTrigger(routine.trigger)) {
|
|
381
|
+
routine.nextRunAt = this.computeNextRun(routine.trigger.cronExpression);
|
|
382
|
+
}
|
|
383
|
+
routine.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
384
|
+
this.upsertRoutine(routine);
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Get all routines that are due to run (nextRunAt <= now and enabled).
|
|
389
|
+
* Filters by scope: "global" or "project".
|
|
390
|
+
*/
|
|
391
|
+
async getDueRoutines(scope) {
|
|
392
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
393
|
+
const rows = this.db.prepare("SELECT * FROM routines WHERE enabled = 1 AND nextRunAt IS NOT NULL AND nextRunAt <= ? AND scope = ?").all(now, scope);
|
|
394
|
+
return rows.map((row) => this.rowToRoutine(row));
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Get all routines that are due to run (nextRunAt <= now and enabled) for both scopes.
|
|
398
|
+
* Returns routines from both "global" and "project" scopes.
|
|
399
|
+
*/
|
|
400
|
+
async getDueRoutinesAllScopes() {
|
|
401
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
402
|
+
const rows = this.db.prepare("SELECT * FROM routines WHERE enabled = 1 AND nextRunAt IS NOT NULL AND nextRunAt <= ?").all(now);
|
|
403
|
+
return rows.map((row) => this.rowToRoutine(row));
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
export {
|
|
408
|
+
RoutineStore
|
|
409
|
+
};
|