mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-08 02:37:45 +02:00
[eric] events: zero-knob pass (adaptive cadence, MCP inference, self-heal, no test nag, one-URL push)
This commit is contained in:
@@ -8,9 +8,6 @@ import { API_BASE } from '@/shared/config';
|
||||
import type { EventTriggerConfig, Workflow } from '@/shared/state/workflowsSlice';
|
||||
import { useWC, track, knob } from './uiKit';
|
||||
|
||||
const WEB_POLL_CHOICES: Array<[number, string]> = [[60, 'every minute'], [300, 'every 5 min'], [900, 'every 15 min'], [3600, 'hourly']];
|
||||
const AGENT_POLL_CHOICES: Array<[number, string]> = [[300, 'every 5 min'], [900, 'every 15 min'], [3600, 'hourly'], [21600, 'every 6 hours'], [86400, 'daily']];
|
||||
|
||||
const KIND_LABELS: Record<string, string> = {
|
||||
file: 'Folder / file watch',
|
||||
web: 'Web page watch',
|
||||
@@ -42,16 +39,6 @@ const EventTriggerRow: React.FC<RowProps> = ({ workflow, trigger, onMutate, onRe
|
||||
cursor: 'pointer', fontSize: 11.5, fontWeight: 600, background: WC.raised, color: WC.ink3,
|
||||
};
|
||||
|
||||
const pollSelect = (value: number, choices: Array<[number, string]>, onChange: (v: number) => void) => (
|
||||
<select
|
||||
value={value}
|
||||
onChange={(e) => onChange(parseInt(e.target.value, 10))}
|
||||
style={{ ...fieldStyle, cursor: 'pointer', padding: '0 6px' }}
|
||||
>
|
||||
{choices.map(([v, label]) => <option key={v} value={v}>{label}</option>)}
|
||||
</select>
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ border: `1px solid ${WC.line}`, borderRadius: 10, padding: '10px 11px', marginBottom: 10 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 9 }}>
|
||||
@@ -97,44 +84,33 @@ const EventTriggerRow: React.FC<RowProps> = ({ workflow, trigger, onMutate, onRe
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<span style={labelStyle}>Watching for</span>
|
||||
<input
|
||||
style={fieldStyle}
|
||||
defaultValue={src.watch_for}
|
||||
placeholder="a reservation slot opening"
|
||||
onBlur={(e) => {
|
||||
const watchFor = e.target.value.trim();
|
||||
if (watchFor !== src.watch_for) onMutate((x) => ({ ...x, source: { ...src, watch_for: watchFor } }));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ width: 118, flex: 'none' }}>
|
||||
<span style={labelStyle}>Check</span>
|
||||
{pollSelect(src.poll_seconds, WEB_POLL_CHOICES, (v) => onMutate((x) => ({ ...x, source: { ...src, poll_seconds: v } })))}
|
||||
</div>
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<span style={labelStyle}>Watching for (checked automatically; speeds up when things happen)</span>
|
||||
<input
|
||||
style={fieldStyle}
|
||||
defaultValue={src.watch_for}
|
||||
placeholder="a reservation slot opening"
|
||||
onBlur={(e) => {
|
||||
const watchFor = e.target.value.trim();
|
||||
if (watchFor !== src.watch_for) onMutate((x) => ({ ...x, source: { ...src, watch_for: watchFor } }));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{src.kind === 'agent' && (
|
||||
<div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<span style={labelStyle}>What counts as the event? An agent checks with its tools.</span>
|
||||
<input
|
||||
style={fieldStyle}
|
||||
defaultValue={src.check}
|
||||
placeholder="a new episode of my favorite podcast is out"
|
||||
onBlur={(e) => {
|
||||
const check = e.target.value.trim();
|
||||
if (check !== src.check) onMutate((x) => ({ ...x, source: { ...src, check } }));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ width: 130, flex: 'none', alignSelf: 'flex-end' }}>
|
||||
{pollSelect(src.poll_seconds, AGENT_POLL_CHOICES, (v) => onMutate((x) => ({ ...x, source: { ...src, poll_seconds: v } })))}
|
||||
</div>
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<span style={labelStyle}>What counts as the event? An agent checks automatically with its tools.</span>
|
||||
<input
|
||||
style={fieldStyle}
|
||||
defaultValue={src.check}
|
||||
placeholder="a new episode of my favorite podcast is out"
|
||||
onBlur={(e) => {
|
||||
const check = e.target.value.trim();
|
||||
if (check !== src.check) onMutate((x) => ({ ...x, source: { ...src, check } }));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -175,7 +151,9 @@ const EventTriggerRow: React.FC<RowProps> = ({ workflow, trigger, onMutate, onRe
|
||||
borderRadius: 8, fontSize: 10.5, color: WC.ink3, whiteSpace: 'pre-wrap', wordBreak: 'break-all',
|
||||
fontFamily: "'JetBrains Mono',monospace", userSelect: 'text',
|
||||
}}>
|
||||
{`POST ${API_BASE}/events/ingest\n{"workflow_id": "${workflow.id}", "trigger_id": "${t.id}", "summary": "what happened", "dedup_key": "optional-id"}`}
|
||||
{src.secret
|
||||
? `POST ${API_BASE}/events/ingest/${src.secret}\n{"summary": "what happened"}`
|
||||
: `POST ${API_BASE}/events/ingest\n{"workflow_id": "${workflow.id}", "trigger_id": "${t.id}", "summary": "what happened"}`}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -23,11 +23,11 @@ const ADD_CHOICES: Array<[TriggerKind, string]> = [
|
||||
];
|
||||
|
||||
function newSource(kind: TriggerKind): EventSourceConfig {
|
||||
if (kind === 'file') return { kind: 'file', path: '', poll_seconds: 15 };
|
||||
if (kind === 'web') return { kind: 'web', url: '', watch_for: '', poll_seconds: 300 };
|
||||
if (kind === 'agent') return { kind: 'agent', check: '', model: '', poll_seconds: 900 };
|
||||
if (kind === 'file') return { kind: 'file', path: '', poll_seconds: 0 };
|
||||
if (kind === 'web') return { kind: 'web', url: '', watch_for: '', poll_seconds: 0 };
|
||||
if (kind === 'agent') return { kind: 'agent', check: '', model: '', poll_seconds: 0 };
|
||||
if (kind === 'stream') return { kind: 'stream', url: '', contains: '' };
|
||||
return { kind: 'custom' };
|
||||
return { kind: 'custom', secret: crypto.randomUUID().replace(/-/g, '') };
|
||||
}
|
||||
|
||||
function newTrigger(kind: TriggerKind): EventTriggerConfig {
|
||||
|
||||
@@ -54,6 +54,8 @@ export interface AgentCheckSource {
|
||||
export interface CustomEventSource {
|
||||
/** Push-only: events arrive via POST /api/events/ingest from any script/webhook/Shortcut. */
|
||||
kind: 'custom';
|
||||
/** Per-trigger push credential; the paste-one-URL form is /api/events/ingest/<secret>. */
|
||||
secret?: string;
|
||||
}
|
||||
|
||||
export interface StreamSource {
|
||||
|
||||
Reference in New Issue
Block a user