beercan 0.3.8 → 0.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beercan",
3
- "version": "0.3.8",
3
+ "version": "0.4.0",
4
4
  "description": "Autonomous AI agent system — powered by Skippy the Magnificent.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -762,6 +762,19 @@
762
762
  </div>
763
763
  </section>
764
764
 
765
+ <!-- ── Schedules ──────────────────────────────────────── -->
766
+ <section class="schedules-section">
767
+ <div class="container">
768
+ <div class="section-label">Schedules</div>
769
+ <h2 class="section-title">Recurring tasks</h2>
770
+ <p class="section-desc">Cron-based bloops that run automatically.</p>
771
+
772
+ <div id="schedules-container">
773
+ <div class="empty-state">No schedules. Add one with Skippy: "schedule daily at 9am: fetch news"</div>
774
+ </div>
775
+ </div>
776
+ </section>
777
+
765
778
  <!-- ── Active Bloops ───────────────────────────────────── -->
766
779
  <section class="active-bloops-section hidden" id="active-bloops-section">
767
780
  <div class="container">
@@ -914,17 +927,19 @@
914
927
  const apiUrl = getApiUrl();
915
928
 
916
929
  try {
917
- const [statusRes, projectsRes, jobsRes, recentRes] = await Promise.all([
930
+ const [statusRes, projectsRes, jobsRes, recentRes, schedulesRes] = await Promise.all([
918
931
  fetch(apiUrl + '/api/status').then(r => r.json()),
919
932
  fetch(apiUrl + '/api/projects').then(r => r.json()),
920
933
  fetch(apiUrl + '/api/jobs').then(r => r.json()),
921
934
  fetch(apiUrl + '/api/bloops/recent').then(r => r.json()),
935
+ fetch(apiUrl + '/api/schedules').then(r => r.json()),
922
936
  ]);
923
937
 
924
938
  setConnected(true);
925
939
  renderOverview(statusRes, jobsRes);
926
940
  renderProjects(projectsRes);
927
941
  renderJobs(jobsRes);
942
+ renderSchedules(schedulesRes);
928
943
  renderActiveBloops(recentRes);
929
944
  renderHistory(recentRes);
930
945
  } catch (err) {
@@ -1029,6 +1044,31 @@
1029
1044
  }
1030
1045
 
1031
1046
  // ── Render: Active Bloops ─────────────────────────
1047
+ // ── Render: Schedules ─────────────────────────────
1048
+ function renderSchedules(data) {
1049
+ const container = document.getElementById('schedules-container');
1050
+ const schedules = data.schedules || [];
1051
+
1052
+ if (schedules.length === 0) {
1053
+ container.innerHTML = '<div class="empty-state">No schedules. Add one with Skippy: "schedule daily at 9am: fetch news"</div>';
1054
+ return;
1055
+ }
1056
+
1057
+ let html = '<div class="history-list">';
1058
+ for (const s of schedules) {
1059
+ const statusDot = s.enabled ? '<span class="dot green"></span>' : '<span class="dot red"></span>';
1060
+ const lastRun = s.lastRunAt ? formatTimestamp(s.lastRunAt) : 'never';
1061
+ html += '<div class="history-item">';
1062
+ html += statusDot;
1063
+ html += '<span class="history-goal"><code>' + escapeHtml(s.cronExpression) + '</code> &mdash; ' + escapeHtml(s.goal) + '</span>';
1064
+ html += '<span class="history-project">' + escapeHtml(s.projectSlug) + '</span>';
1065
+ html += '<span class="history-duration">Last: ' + lastRun + '</span>';
1066
+ html += '</div>';
1067
+ }
1068
+ html += '</div>';
1069
+ container.innerHTML = html;
1070
+ }
1071
+
1032
1072
  function renderActiveBloops(data) {
1033
1073
  const bloops = (data.bloops || []).filter(b => b.status === 'running');
1034
1074
  const section = document.getElementById('active-bloops-section');