nomoreide 0.1.64 → 0.1.68

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.
@@ -0,0 +1,149 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { z } from "zod";
3
+ /**
4
+ * Event-driven workflow triggers (IDEAS #16). Workflows are user-run today; a
5
+ * trigger binds an app-detected event to a workflow so it fires automatically.
6
+ *
7
+ * The workflow runner lives client-side (agent steps run through the dock), so a
8
+ * trigger can't run a workflow on the server. Instead a fired trigger enqueues a
9
+ * **pending run** here; the open dashboard drains the queue and drives the
10
+ * existing client runner. Pending runs survive until a browser claims them, so
11
+ * an event detected while no tab is open isn't lost.
12
+ *
13
+ * Live event sources are the two already push-based and in-process:
14
+ * - `error-incident` — {@link ErrorInbox} surfaces a new/repeated incident.
15
+ * - `service-crash` — {@link TimelineStore} records a `service.lifecycle`
16
+ * event with `severity: "error"` (non-zero exit not initiated by the user).
17
+ */
18
+ export const triggerEventSchema = z.enum(["error-incident", "service-crash"]);
19
+ export const workflowTriggerSchema = z.object({
20
+ id: z.string().min(1),
21
+ /** The workflow (built-in or user-saved) to run when this fires. */
22
+ workflowId: z.string().min(1),
23
+ event: triggerEventSchema,
24
+ /** Disabled triggers stay configured but never fire. */
25
+ enabled: z.boolean().default(true),
26
+ /**
27
+ * Optional case-insensitive substring matched against the event's service
28
+ * name (and, for incidents, the incident title). Empty/absent = match all.
29
+ */
30
+ filter: z.string().optional(),
31
+ /**
32
+ * When true the dashboard starts the run automatically; otherwise the pending
33
+ * run is surfaced for the user to start with one click.
34
+ */
35
+ autoRun: z.boolean().default(false),
36
+ });
37
+ export class WorkflowTriggerManager {
38
+ configStore;
39
+ errorInbox;
40
+ timelineStore;
41
+ capacity;
42
+ dedupeWindowMs;
43
+ now;
44
+ pending = [];
45
+ lastFiredAt = new Map();
46
+ listeners = new Set();
47
+ unsubscribers = [];
48
+ started = false;
49
+ constructor(options) {
50
+ this.configStore = options.configStore;
51
+ this.errorInbox = options.errorInbox;
52
+ this.timelineStore = options.timelineStore;
53
+ this.capacity = options.capacity ?? 50;
54
+ this.dedupeWindowMs = options.dedupeWindowMs ?? 30_000;
55
+ this.now = options.now ?? Date.now;
56
+ }
57
+ /** Subscribe to the live event sources. Idempotent. */
58
+ start() {
59
+ if (this.started)
60
+ return;
61
+ this.started = true;
62
+ this.unsubscribers.push(this.errorInbox.subscribe((incident) => {
63
+ void this.onIncident(incident);
64
+ }));
65
+ this.unsubscribers.push(this.timelineStore.subscribe((event) => {
66
+ void this.onTimelineEvent(event);
67
+ }));
68
+ }
69
+ stop() {
70
+ for (const off of this.unsubscribers.splice(0))
71
+ off();
72
+ this.started = false;
73
+ }
74
+ subscribe(listener) {
75
+ this.listeners.add(listener);
76
+ return () => this.listeners.delete(listener);
77
+ }
78
+ /** Pending runs, newest first. */
79
+ listPending() {
80
+ return [...this.pending].reverse();
81
+ }
82
+ /** Drop a pending run once the dashboard has started (or dismissed) it. */
83
+ ackPending(id) {
84
+ const index = this.pending.findIndex((run) => run.id === id);
85
+ if (index === -1)
86
+ return false;
87
+ this.pending.splice(index, 1);
88
+ return true;
89
+ }
90
+ async onIncident(incident) {
91
+ const triggers = await this.triggersFor("error-incident");
92
+ const haystack = `${incident.service} ${incident.title}`;
93
+ for (const trigger of triggers) {
94
+ if (!matchesFilter(trigger.filter, haystack))
95
+ continue;
96
+ this.enqueue(trigger, {
97
+ signatureCause: `incident:${incident.signature}`,
98
+ summary: `${incident.service}: ${incident.title}`,
99
+ });
100
+ }
101
+ }
102
+ async onTimelineEvent(event) {
103
+ if (event.kind !== "service.lifecycle" || event.severity !== "error")
104
+ return;
105
+ const service = event.service ?? "";
106
+ const triggers = await this.triggersFor("service-crash");
107
+ for (const trigger of triggers) {
108
+ if (!matchesFilter(trigger.filter, service))
109
+ continue;
110
+ this.enqueue(trigger, {
111
+ signatureCause: `crash:${service}`,
112
+ summary: event.title || `${service} crashed`,
113
+ });
114
+ }
115
+ }
116
+ async triggersFor(event) {
117
+ const config = await this.configStore.load();
118
+ return config.workflowTriggers.filter((trigger) => trigger.enabled && trigger.event === event);
119
+ }
120
+ enqueue(trigger, cause) {
121
+ const signature = `${trigger.id}:${cause.signatureCause}`;
122
+ const at = this.now();
123
+ const previous = this.lastFiredAt.get(signature);
124
+ if (previous !== undefined && at - previous < this.dedupeWindowMs)
125
+ return;
126
+ this.lastFiredAt.set(signature, at);
127
+ const run = {
128
+ id: randomUUID(),
129
+ triggerId: trigger.id,
130
+ workflowId: trigger.workflowId,
131
+ event: trigger.event,
132
+ summary: cause.summary,
133
+ signature,
134
+ autoRun: trigger.autoRun,
135
+ createdAt: new Date(at).toISOString(),
136
+ };
137
+ this.pending.push(run);
138
+ this.pending.splice(0, Math.max(0, this.pending.length - this.capacity));
139
+ for (const listener of this.listeners)
140
+ listener(run);
141
+ }
142
+ }
143
+ function matchesFilter(filter, haystack) {
144
+ const needle = filter?.trim().toLowerCase();
145
+ if (!needle)
146
+ return true;
147
+ return haystack.toLowerCase().includes(needle);
148
+ }
149
+ //# sourceMappingURL=workflow-triggers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-triggers.js","sourceRoot":"","sources":["../../src/core/workflow-triggers.ts"],"names":[],"mappings":"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"}