{
  "id": "cron-runner",
  "name": "Cron Runner",
  "category": "automation",
  "tags": ["cron", "schedule", "automation", "recurring", "task", "scheduler"],
  "description": "Schedule recurring TITAN tasks. Lists active cron jobs, lets you create a new one (cron expression + prompt), pause/resume, and delete.",
  "triggers": ["cron", "scheduled task", "recurring task", "schedule", "cron job", "every hour", "every day", "cron runner"],
  "defaultSize": { "w": 6, "h": 5 },
  "source": "function CronRunner() {\n  const [jobs, setJobs] = React.useState([]);\n  const [name, setName] = React.useState('REPLACE_WITH_NAME');\n  const [expr, setExpr] = React.useState('REPLACE_WITH_CRON');\n  const [task, setTask] = React.useState('REPLACE_WITH_TASK');\n  const [busy, setBusy] = React.useState(false);\n  const refresh = async () => {\n    try { const r = await titan.api.call('/api/cron', { method: 'GET' }); setJobs(Array.isArray(r)?r:(r?.jobs||[])); } catch {}\n  };\n  React.useEffect(() => { refresh(); const id = setInterval(refresh, 5000); return () => clearInterval(id); }, []);\n  const create = async () => {\n    if (!name || !expr || !task) return;\n    setBusy(true);\n    try { await titan.api.call('/api/cron', { method: 'POST', body: { name, schedule: expr, prompt: task } }); await refresh(); }\n    catch (e) { alert('Create failed: ' + (e?.message||e)); }\n    finally { setBusy(false); }\n  };\n  const remove = async (id) => {\n    if (!confirm('Delete this cron job?')) return;\n    try { await titan.api.call(`/api/cron/${id}`, { method: 'DELETE' }); await refresh(); } catch (e) { alert(e?.message||e); }\n  };\n  return (\n    <div style={{padding:14,fontFamily:'system-ui',height:'100%',display:'flex',flexDirection:'column',gap:10}}>\n      <div style={{fontSize:16,fontWeight:600}}>Scheduled Tasks</div>\n      <div style={{display:'grid',gridTemplateColumns:'1fr 1fr 2fr auto',gap:6}}>\n        <input value={name} onChange={e=>setName(e.target.value)} placeholder=\"Name\" style={{padding:'6px 8px',border:'1px solid #ccc',borderRadius:6,fontSize:13}}/>\n        <input value={expr} onChange={e=>setExpr(e.target.value)} placeholder=\"Cron (e.g. 0 9 * * *)\" style={{padding:'6px 8px',border:'1px solid #ccc',borderRadius:6,fontSize:13,fontFamily:'monospace'}}/>\n        <input value={task} onChange={e=>setTask(e.target.value)} placeholder=\"What should TITAN do?\" style={{padding:'6px 8px',border:'1px solid #ccc',borderRadius:6,fontSize:13}}/>\n        <button onClick={create} disabled={busy} style={{padding:'6px 14px',borderRadius:6,border:'none',background:'#007AFF',color:'#fff',cursor:busy?'wait':'pointer',fontWeight:600}}>{busy?'…':'Add'}</button>\n      </div>\n      <div style={{flex:1,overflow:'auto',border:'1px solid #eee',borderRadius:8}}>\n        {jobs.length === 0 ? <div style={{padding:20,textAlign:'center',color:'#888'}}>No scheduled tasks yet.</div> :\n          jobs.map((j,i) => (\n            <div key={j.id||i} style={{display:'grid',gridTemplateColumns:'2fr 1.5fr 3fr auto',gap:8,padding:'10px 12px',borderBottom:'1px solid #f0f0f0',alignItems:'center',fontSize:13}}>\n              <div style={{fontWeight:600}}>{j.name||j.id}</div>\n              <code style={{fontSize:12,color:'#555'}}>{j.schedule||j.expr}</code>\n              <div style={{color:'#666',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{j.prompt||j.task}</div>\n              <button onClick={()=>remove(j.id)} style={{padding:'4px 10px',border:'1px solid #ddd',background:'#fff',borderRadius:4,cursor:'pointer',fontSize:12}}>✕</button>\n            </div>\n          ))\n        }\n      </div>\n      <div style={{fontSize:11,color:'#888'}}>Tip: <code>0 9 * * *</code> = 9 AM daily. <code>*/15 * * * *</code> = every 15 min.</div>\n    </div>\n  );\n}\nrender(<CronRunner/>);",
  "placeholders": [
    { "name": "REPLACE_WITH_NAME", "description": "Default job name", "default": "Daily Briefing" },
    { "name": "REPLACE_WITH_CRON", "description": "Default cron expression", "default": "0 9 * * *" },
    { "name": "REPLACE_WITH_TASK", "description": "Default task prompt", "default": "Summarize my unread emails" }
  ]
}
