chainlesschain 0.163.5 → 0.163.6
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/completions/_cc +1 -1
- package/completions/cc.bash +1 -1
- package/completions/cc.fish +1 -1
- package/completions/cc.ps1 +1 -1
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-Cv2EE1Zy.js → AIOps-DDIUofA8.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CcpxMLuW.js → ActionButton-Bm-5xYRn.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DE8mJoMI.js → Analytics-D5UH4ljI.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DKn9dk9L.js → AppLayout-D_ifjOOP.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-Cp3mG3Py.js → Artifacts-BY1mXdC7.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-zvsQfF2C.js → Audit-Bujr4Ml3.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-Bi85SsjN.js → BackgroundAgents-sioKSTcC.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Cw47Dj6s.js → Backup-DDOAK8RK.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C5Ip2afH.js → BaseInput-CtCsVVke.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-C0iAFkJv.js → Chat-DpNj0vnd.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-BV9Wv7wO.js → ChatBubbleRenderer-CZta3Ftr.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-3KZZCiAF.js → Checkbox-DoHVNSXq.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-C8OMbBAz.js → Codegen-DlVl7Qmv.js} +1 -1
- package/src/assets/web-panel/assets/{Col-B-MB84LJ.js → Col-GIqoPYty.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DpR7Dxao.js → Community-CIAPEVji.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CyDtz0OS.js → Compact-BN7JiI_h.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-5tiRa2An.js → Compliance-CqUWCM9B.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DnQaKZ5x.js → Cowork-A4iPp7A4.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-BaWfoEDf.js → Cron-B2zeXXWb.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-Bu0HoHhb.js → Crosschain-BR2xmWr8.js} +1 -1
- package/src/assets/web-panel/assets/{DID-31zk8upX.js → DID-BzFDjrIv.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BmVne-e2.js → Dashboard-DKtDo3yA.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-B4PkoOWd.js → Dropdown-RNx43_YG.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BZ6tyFtI.js → EmailListRenderer-SwMt4Rcy.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Osozn4Ds.js → FamilyGuardDashboard-CsYebZ4X.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DaH8TOAP.js → Federation-BmRKvBTy.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-tbU0uwis.js → FormItemContext-CVxGTA-0.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-Df8LQBrL.js → GenericCardRenderer-x2iXpyB7.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DsMB6-14.js → Git-DfqbkiOP.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BnH0dZzd.js → Governance-RuHyyj5O.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DhnN1N_P.js → Inference-Bn7uZ7rI.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DnNshAgn.js → KnowledgeGraph-DlP7XefZ.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BmBFX3_N.js → Logs-BYNdOiCd.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-DCslfQU3.js → MarkdownRenderer-aMtgN-R6.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-CPEgWDc3.js → Marketplace-JQYmDqM8.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-C36qjgF9.js → McpTools-Cyx4yZ-9.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-tNHvrgqN.js → Memory-VDuGZTN2.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-Xaf6cKjc.js → MobileBridge-_yKo97gi.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-D2E5TTeO.js → MobileProjects-CXRXW3q7.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-D4AJ2dwV.js → Mtc-acTGtfLH.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-Df-gtn2_.js → MtcAudit-ClpjXfkY.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-DTCHLWld.js → Multisig-_6Tn3o96.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DruZ_1Jb.js → NLProgramming-CK7pbZzP.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Dx3pdGDs.js → Notes-Czy6V_P8.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-Bi3L69oL.js → NotificationSettings-Cqx-UVv3.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BHY33m7C.js → OrderTableRenderer-C69WDxGy.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BG2ZVEV-.js → Organization-DFnA9vLQ.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Viwtg4Mn.js → Overflow-ReZkk2yT.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Clm_yc1Z.js → P2P-DzjSI0aI.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-NKmmDiOw.js → PdhVaultBrowser-DKTf5xIP.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-DIL8u9Nk.js → Permissions-DhQ5klhR.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-B-jM4wt-.js → PersonalDataHub-CUL1iHrW.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-627I__H5.js → Pipeline-DU88iu4N.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-2DN2qrkW.js → Privacy-Bw-sYfyK.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CN4wC0sF.js → ProjectInit-Bq3Q5y0z.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-K35vsh5H.js → ProjectSettings-C8spzpho.js} +2 -2
- package/src/assets/web-panel/assets/Projects-Dd-xuPIc.js +1 -0
- package/src/assets/web-panel/assets/{Providers-MckaQUhQ.js → Providers-Bf4eTP9W.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-BS3mtA_O.js → QrScannerModal-Dme9BDU1.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BHp_nofL.js → QuickAsk-CIXfv1rE.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DlmnPuZj.js → Recommend-CIxvdeZ-.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-tsyFbvpy.js → RemoteSession-C6k7MExI.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-Ba1iWv2D.js → Reputation-CISDz2bl.js} +1 -1
- package/src/assets/web-panel/assets/{Row-NnlGC--f.js → Row-CdnDxXyf.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CjThSIRv.js → RssFeed-C-QpRHsl.js} +3 -3
- package/src/assets/web-panel/assets/{Search-CojUyC5x.js → Search-DBjaGzSn.js} +1 -1
- package/src/assets/web-panel/assets/{Security-c-sxeJEc.js → Security-CQKukOeE.js} +3 -3
- package/src/assets/web-panel/assets/{Services-CtcKEEmF.js → Services-DZz86jWb.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CIBBXflN.js → Skeleton-jb8RMK0d.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-D1XOOK9n.js → Skills-CdY76fZG.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-hRfEp5Sn.js → Sla-C_CSiZX9.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-C7tT9iad.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-BJsSFGYy.js → SyncSettings-qvimGO0O.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CfSZ8A1L.js → Tasks-Dng5LPJC.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-idDf47W-.js → Templates-Bwqq5Wll.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-C-UsX6w6.js → Tenant-DNWuwP7Z.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BgJtX5n1.js → Terminal-B-6Othpp.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DP0BLKTl.js → TimelineRenderer-DKpKZfzx.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-LwCzqETp.js → Tokens-DzfGMd2J.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-D_9tVch0.js → Trigger-BXwUi74J.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BH2UlEWh.js → Trust-BNzUq4ui.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CIpD0nYb.js → UkeySign-BYugxww4.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-9ShhYxK_.js → VideoEditing-DdauhtSJ.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-C7afGNIG.js → Wallet-CSC9bCe_.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-bUKn6i5R.js → WebAuthn-Q8AN3bTO.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-CxgQaGyp.js → WorkflowEditor-d-LBGCuA.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DMVkmJwx.js → chat-BLVDCgsZ.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CDzQh5nS.js → colors-BZlPSEHl.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-C51kEF4D.js → compact-item-IWseP6R6.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BTk9bXBn.js → createContext-DnVRV3Ao.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-Jzu62CLC.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CMje33rb.js → hasIn-DLQZA_Wn.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBz9LIMS.js → index-1jeAudHd.js} +1 -1
- package/src/assets/web-panel/assets/{index-hg_emBsi.js → index-BEu2lMBy.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRG-aqOs.js → index-BOZJBTWZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CDLkNkLj.js → index-BSjilZ9L.js} +1 -1
- package/src/assets/web-panel/assets/{index-CvSeVzqe.js → index-BZs5bBnk.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpcrFxMI.js → index-B_N4-Gq9.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9rC-fGW.js → index-Bbj2NS_I.js} +3 -3
- package/src/assets/web-panel/assets/{index-BVePe_Rf.js → index-BhA3QEni.js} +1 -1
- package/src/assets/web-panel/assets/{index-BzCkNylB.js → index-Bxp7uDD8.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAfY7m7i.js → index-C1ifZtXo.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTlsE8Df.js → index-C2FqfMvU.js} +1 -1
- package/src/assets/web-panel/assets/{index-DqU9njjT.js → index-C5dgnilC.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtlEqHLP.js → index-CINJQEj6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BeEaAEIp.js → index-COev0W2g.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCLj9hcD.js → index-CT9vVN-X.js} +1 -1
- package/src/assets/web-panel/assets/{index-h31MwrME.js → index-CZDekj7T.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ld_4mFmC.js → index-CarMTIyQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CwGPwjUu.js → index-CbQNL0ue.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5IWTMVl.js → index-CpBaJ3TD.js} +1 -1
- package/src/assets/web-panel/assets/{index-BW4F5YDF.js → index-DB0MJgF_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CypsfcEq.js → index-DOoQpuQY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfC-piWZ.js → index-DTbxp4UH.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCRvG9lq.js → index-DUq0c7gq.js} +1 -1
- package/src/assets/web-panel/assets/{index-0Auljb10.js → index-DZpYeqrh.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4rQCS8T.js → index-D_LQodeJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CtM8OT_7.js → index-DalA0TKC.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1oZQmAA.js → index-DjWnONdy.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRLQyp2A.js → index-Dkwsi1Kn.js} +1 -1
- package/src/assets/web-panel/assets/{index-togc_uYn.js → index-DocEhinm.js} +1 -1
- package/src/assets/web-panel/assets/{index-x53Vf7la.js → index-DpVrK6fr.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgivOKHA.js → index-Dy0R5ZED.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0-zMtKA.js → index-M7wxfAbm.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXQBPLyE.js → index-N_GoWZX4.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1oZReaY.js → index-YSw_gxlZ.js} +1 -1
- package/src/assets/web-panel/assets/index-Zb2MeDSy.js +1 -0
- package/src/assets/web-panel/assets/index-fikqml4k.js +1 -0
- package/src/assets/web-panel/assets/{index-Cd0hmPxD.js → index-gt8bNefW.js} +1 -1
- package/src/assets/web-panel/assets/{index-OS-vfbTA.js → index-hwVDeoC0.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJfnCuMq.js → index-jWb70ZgZ.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-vrA8K-ac.js → initDefaultProps-Co67Kqmb.js} +1 -1
- package/src/assets/web-panel/assets/{motion-ByEzwIAl.js → motion-BMOxQSF_.js} +1 -1
- package/src/assets/web-panel/assets/{move-Ck4AWjw-.js → move-C5fWDw-N.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-6p0l9SrB.js → mtc-parser-BJevpL0e.js} +1 -1
- package/src/assets/web-panel/assets/{omit-B1CUPQ6R.js → omit-BCzNU8s6.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-B1STg1iW.js → pickAttrs-CybWvDPk.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DTTllkBV.js → placementArrow-BMWS9HnW.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-AIrrCfBb.js → responsiveObserve-BoL2UnGY.js} +1 -1
- package/src/assets/web-panel/assets/{slide-C_fPMjdR.js → slide-DWNfXraU.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-D5ow2yIP.js → statusUtils-BquTM3tX.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Dy3hEA--.js → styleChecker-BEkCleim.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-WfzcfSh4.js → useFlexGapSupport-Oecnq7EA.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CKSejfas.js → useFs-CkDffJ4F.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-od3zpPBN.js → usePersonalDataHub-qbvKXesB.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DRD_2DFA.js → vnode-C6v8n6mA.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DmNrEtgf.js → zoom-4CTeFILV.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +3 -3
- package/src/command-manifest.json +1 -1
- package/src/commands/agenda.js +76 -4
- package/src/commands/automation.js +462 -3
- package/src/commands/background-session.js +102 -1
- package/src/commands/cowork.js +6 -1
- package/src/commands/loop.js +167 -36
- package/src/commands/routine.js +11 -4
- package/src/commands/scheduler-daemon.js +450 -0
- package/src/data/changelog.json +15 -2
- package/src/lib/agent-schedule-store.js +323 -43
- package/src/lib/automation-center-routines.js +363 -0
- package/src/lib/automation-center.js +435 -0
- package/src/lib/automation-engine.js +74 -19
- package/src/lib/automation-execution-authority.js +736 -0
- package/src/lib/background-agent-supervisor.js +78 -0
- package/src/lib/background-needs-input-incident.js +221 -0
- package/src/lib/cowork-cron.js +316 -18
- package/src/lib/cowork-observe.js +5 -1
- package/src/lib/loop.js +37 -2
- package/src/lib/permission-engine.js +14 -7
- package/src/lib/public-ide-capability-manifest.js +2 -0
- package/src/lib/routine-store.js +166 -65
- package/src/lib/scheduler-kernel/agenda-adapter.js +297 -27
- package/src/lib/scheduler-kernel/authority-resolver.js +163 -0
- package/src/lib/scheduler-kernel/automation-adapter.js +592 -0
- package/src/lib/scheduler-kernel/automation-event-adapter.js +721 -0
- package/src/lib/scheduler-kernel/contract.js +11 -3
- package/src/lib/scheduler-kernel/cowork-cron-adapter.js +58 -12
- package/src/lib/scheduler-kernel/loop-adapter.js +378 -0
- package/src/lib/scheduler-kernel/routine-adapter.js +303 -8
- package/src/lib/scheduler-kernel/service.js +328 -0
- package/src/lib/scheduler-kernel/store.js +828 -96
- package/src/runtime/agent-core.js +2 -1
- package/src/runtime/coding-agent-contract-shared.cjs +5 -0
- package/src/workers/background-agent-worker.js +91 -28
- package/src/assets/web-panel/assets/Projects-B_wQadhX.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-Dexb1f_6.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CdwPwYj8.js +0 -1
- package/src/assets/web-panel/assets/index-BEeOdfEe.js +0 -1
- package/src/assets/web-panel/assets/index-BTe0TOu6.js +0 -1
package/src/lib/routine-store.js
CHANGED
|
@@ -27,6 +27,11 @@ import path from "node:path";
|
|
|
27
27
|
import os from "node:os";
|
|
28
28
|
import { randomBytes } from "node:crypto";
|
|
29
29
|
import { parseCron, nextCronTime } from "./agent-schedule-store.js";
|
|
30
|
+
import {
|
|
31
|
+
readSecurityStore,
|
|
32
|
+
writeSecurityStore,
|
|
33
|
+
} from "./durable-security-store.js";
|
|
34
|
+
import { withFileLock } from "./with-file-lock.js";
|
|
30
35
|
|
|
31
36
|
export const ROUTINE_TRIGGER_KINDS = Object.freeze([
|
|
32
37
|
"cron",
|
|
@@ -45,11 +50,75 @@ function shortId() {
|
|
|
45
50
|
|
|
46
51
|
function parseWhen(value) {
|
|
47
52
|
if (value == null) return NaN;
|
|
53
|
+
if (typeof value === "string" && value.trim() === "") return NaN;
|
|
54
|
+
if (typeof value !== "string" && typeof value !== "number") return NaN;
|
|
48
55
|
const n = Number(value);
|
|
49
56
|
if (Number.isFinite(n)) return n;
|
|
50
57
|
return Date.parse(String(value));
|
|
51
58
|
}
|
|
52
59
|
|
|
60
|
+
function boundedDefinitionText(value, field, maximum) {
|
|
61
|
+
const text = String(value || "").trim();
|
|
62
|
+
if (!text) throw new Error(`routine needs a ${field}`);
|
|
63
|
+
if (Buffer.byteLength(text, "utf8") > maximum) {
|
|
64
|
+
throw new Error(`routine ${field} exceeds ${maximum} UTF-8 bytes`);
|
|
65
|
+
}
|
|
66
|
+
return text;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function normalizeRoutineDefinition(def = {}) {
|
|
70
|
+
const name = boundedDefinitionText(def.name, "name", 512);
|
|
71
|
+
const prompt = boundedDefinitionText(def.prompt, "prompt", 64 * 1024);
|
|
72
|
+
const raw = def.trigger;
|
|
73
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
74
|
+
throw new Error("routine trigger is required");
|
|
75
|
+
}
|
|
76
|
+
if (!ROUTINE_TRIGGER_KINDS.includes(raw.kind)) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`routine trigger kind must be one of: ${ROUTINE_TRIGGER_KINDS.join(", ")}`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
let trigger;
|
|
82
|
+
if (raw.kind === "cron") {
|
|
83
|
+
const cron = String(raw.cron || "").trim();
|
|
84
|
+
if (!parseCron(cron)) throw new Error(`invalid cron expression: ${cron}`);
|
|
85
|
+
trigger = { kind: "cron", cron };
|
|
86
|
+
} else if (raw.kind === "once") {
|
|
87
|
+
const at = parseWhen(raw.at);
|
|
88
|
+
if (!Number.isFinite(at) || !Number.isSafeInteger(at) || at < 0) {
|
|
89
|
+
throw new Error(`invalid --at time: ${raw.at}`);
|
|
90
|
+
}
|
|
91
|
+
trigger = { kind: "once", at };
|
|
92
|
+
} else if (raw.kind === "github") {
|
|
93
|
+
const repo = String(raw.repo || "").trim();
|
|
94
|
+
const parts = repo.split("/");
|
|
95
|
+
if (
|
|
96
|
+
!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u.test(repo) ||
|
|
97
|
+
parts.some((part) => part === "." || part === "..")
|
|
98
|
+
) {
|
|
99
|
+
throw new Error("github trigger needs --repo <owner/name>");
|
|
100
|
+
}
|
|
101
|
+
const events = Array.isArray(raw.events)
|
|
102
|
+
? [...new Set(raw.events.map((value) => String(value || "").trim()))]
|
|
103
|
+
.filter(Boolean)
|
|
104
|
+
.sort()
|
|
105
|
+
: [];
|
|
106
|
+
if (
|
|
107
|
+
events.length > 64 ||
|
|
108
|
+
events.some(
|
|
109
|
+
(event) =>
|
|
110
|
+
event.length > 128 || !/^[A-Za-z][A-Za-z0-9_.-]*$/u.test(event),
|
|
111
|
+
)
|
|
112
|
+
) {
|
|
113
|
+
throw new Error("github trigger events are invalid");
|
|
114
|
+
}
|
|
115
|
+
trigger = { kind: "github", repo, events };
|
|
116
|
+
} else {
|
|
117
|
+
trigger = { kind: "webhook" };
|
|
118
|
+
}
|
|
119
|
+
return { name, prompt, trigger };
|
|
120
|
+
}
|
|
121
|
+
|
|
53
122
|
export class RoutineStore {
|
|
54
123
|
constructor({ dir = null, now = () => Date.now() } = {}) {
|
|
55
124
|
this.dir = dir || routinesDir();
|
|
@@ -70,68 +139,58 @@ export class RoutineStore {
|
|
|
70
139
|
}
|
|
71
140
|
|
|
72
141
|
_readRoutines() {
|
|
73
|
-
|
|
74
|
-
const parsed = JSON.parse(fs.readFileSync(this._routinesFile(), "utf-8"));
|
|
75
|
-
return parsed && typeof parsed === "object" ? parsed : {};
|
|
76
|
-
} catch {
|
|
77
|
-
return {};
|
|
78
|
-
}
|
|
142
|
+
return readSecurityStore(this._routinesFile(), "routine definitions");
|
|
79
143
|
}
|
|
80
144
|
_writeRoutines(map) {
|
|
145
|
+
writeSecurityStore(this._routinesFile(), "routine definitions", map);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
_withDefinitionLock(callback) {
|
|
81
149
|
this._ensureDir();
|
|
82
|
-
|
|
150
|
+
return withFileLock(
|
|
83
151
|
this._routinesFile(),
|
|
84
|
-
|
|
85
|
-
|
|
152
|
+
() => callback(this._readRoutines()),
|
|
153
|
+
{ failIfUnavailable: true },
|
|
86
154
|
);
|
|
87
155
|
}
|
|
88
156
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (!prompt) throw new Error("routine needs a prompt");
|
|
98
|
-
if (!ROUTINE_TRIGGER_KINDS.includes(trigger.kind)) {
|
|
99
|
-
throw new Error(
|
|
100
|
-
`routine trigger kind must be one of: ${ROUTINE_TRIGGER_KINDS.join(", ")}`,
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
if (trigger.kind === "cron") {
|
|
104
|
-
if (!parseCron(trigger.cron || "")) {
|
|
105
|
-
throw new Error(`invalid cron expression: ${trigger.cron}`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
if (trigger.kind === "once") {
|
|
109
|
-
const at = parseWhen(trigger.at);
|
|
110
|
-
if (!Number.isFinite(at)) {
|
|
111
|
-
throw new Error(`invalid --at time: ${trigger.at}`);
|
|
112
|
-
}
|
|
113
|
-
trigger.at = at;
|
|
114
|
-
}
|
|
115
|
-
if (trigger.kind === "github" && !trigger.repo) {
|
|
116
|
-
throw new Error("github trigger needs --repo <owner/name>");
|
|
157
|
+
_resolveFromMap(map, id) {
|
|
158
|
+
if (map[id]) return map[id];
|
|
159
|
+
const matches = Object.values(map).filter(
|
|
160
|
+
(routine) => routine.id.startsWith(id) || routine.name === id,
|
|
161
|
+
);
|
|
162
|
+
if (matches.length === 1) return matches[0];
|
|
163
|
+
if (matches.length > 1) {
|
|
164
|
+
throw new Error(`routine "${id}" is ambiguous (${matches.length} match)`);
|
|
117
165
|
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
118
168
|
|
|
169
|
+
_createInMap(map, def) {
|
|
170
|
+
const definition = normalizeRoutineDefinition(def);
|
|
119
171
|
const routine = {
|
|
120
172
|
id: shortId(),
|
|
121
|
-
|
|
122
|
-
prompt,
|
|
123
|
-
trigger,
|
|
173
|
+
...definition,
|
|
124
174
|
enabled: true,
|
|
125
175
|
createdAt: this._now(),
|
|
126
176
|
lastFiredAt: null,
|
|
127
177
|
lastSeenGithubEventId: null,
|
|
128
178
|
};
|
|
129
|
-
const map = this._readRoutines();
|
|
130
179
|
map[routine.id] = routine;
|
|
131
|
-
this._writeRoutines(map);
|
|
132
180
|
return routine;
|
|
133
181
|
}
|
|
134
182
|
|
|
183
|
+
/**
|
|
184
|
+
* @param {object} def { name, prompt, trigger: {kind, cron?, at?, repo?, events?} }
|
|
185
|
+
*/
|
|
186
|
+
create(def = {}) {
|
|
187
|
+
return this._withDefinitionLock((map) => {
|
|
188
|
+
const routine = this._createInMap(map, def);
|
|
189
|
+
this._writeRoutines(map);
|
|
190
|
+
return routine;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
135
194
|
list() {
|
|
136
195
|
return Object.values(this._readRoutines()).sort(
|
|
137
196
|
(a, b) => (b.createdAt || 0) - (a.createdAt || 0),
|
|
@@ -140,26 +199,26 @@ export class RoutineStore {
|
|
|
140
199
|
|
|
141
200
|
get(id) {
|
|
142
201
|
const map = this._readRoutines();
|
|
143
|
-
|
|
144
|
-
// prefix / name match for CLI ergonomics
|
|
145
|
-
const matches = Object.values(map).filter(
|
|
146
|
-
(r) => r.id.startsWith(id) || r.name === id,
|
|
147
|
-
);
|
|
148
|
-
if (matches.length === 1) return matches[0];
|
|
149
|
-
if (matches.length > 1) {
|
|
150
|
-
throw new Error(`routine "${id}" is ambiguous (${matches.length} match)`);
|
|
151
|
-
}
|
|
152
|
-
return null;
|
|
202
|
+
return this._resolveFromMap(map, id);
|
|
153
203
|
}
|
|
154
204
|
|
|
155
205
|
update(id, patch) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
206
|
+
return this._withDefinitionLock((map) => {
|
|
207
|
+
const routine = this._resolveFromMap(map, id);
|
|
208
|
+
if (!routine) throw new Error(`routine not found: ${id}`);
|
|
209
|
+
const next = { ...routine, ...patch, id: routine.id };
|
|
210
|
+
map[routine.id] = next;
|
|
211
|
+
this._writeRoutines(map);
|
|
212
|
+
return next;
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
updateDefinition(id, definition) {
|
|
217
|
+
const normalized = normalizeRoutineDefinition(definition);
|
|
218
|
+
return this.update(id, {
|
|
219
|
+
...normalized,
|
|
220
|
+
lastSeenGithubEventId: null,
|
|
221
|
+
});
|
|
163
222
|
}
|
|
164
223
|
|
|
165
224
|
setEnabled(id, enabled) {
|
|
@@ -167,12 +226,54 @@ export class RoutineStore {
|
|
|
167
226
|
}
|
|
168
227
|
|
|
169
228
|
remove(id) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
229
|
+
return this._withDefinitionLock((map) => {
|
|
230
|
+
const routine = this._resolveFromMap(map, id);
|
|
231
|
+
if (!routine) throw new Error(`routine not found: ${id}`);
|
|
232
|
+
delete map[routine.id];
|
|
233
|
+
this._writeRoutines(map);
|
|
234
|
+
return routine;
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
createIfRevision(expectedRevision, revisionOfMap, definition) {
|
|
239
|
+
return this._withDefinitionLock((map) => {
|
|
240
|
+
if (revisionOfMap(map) !== expectedRevision) {
|
|
241
|
+
const error = new Error("routine collection changed before create");
|
|
242
|
+
error.code = "ROUTINE_REVISION_CONFLICT";
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
const routine = this._createInMap(map, definition);
|
|
246
|
+
this._writeRoutines(map);
|
|
247
|
+
return routine;
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
readIfRevision(id, expectedRevision, revisionOfRoutine) {
|
|
252
|
+
return this._withDefinitionLock((map) => {
|
|
253
|
+
const routine = map[id];
|
|
254
|
+
if (!routine) throw new Error(`routine not found: ${id}`);
|
|
255
|
+
if (revisionOfRoutine(routine) !== expectedRevision) {
|
|
256
|
+
const error = new Error(`routine changed before action: ${id}`);
|
|
257
|
+
error.code = "ROUTINE_REVISION_CONFLICT";
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
return structuredClone(routine);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
mutateIfRevision(id, expectedRevision, revisionOfRoutine, mutation) {
|
|
265
|
+
return this._withDefinitionLock((map) => {
|
|
266
|
+
const routine = map[id];
|
|
267
|
+
if (!routine) throw new Error(`routine not found: ${id}`);
|
|
268
|
+
if (revisionOfRoutine(routine) !== expectedRevision) {
|
|
269
|
+
const error = new Error(`routine changed before mutation: ${id}`);
|
|
270
|
+
error.code = "ROUTINE_REVISION_CONFLICT";
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
const result = mutation(routine, map);
|
|
274
|
+
this._writeRoutines(map);
|
|
275
|
+
return result;
|
|
276
|
+
});
|
|
176
277
|
}
|
|
177
278
|
|
|
178
279
|
/** Routines the driver should fire now (cron due / once reached). */
|