@kky42/pievo-server 2.4.2 → 2.4.5

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.
Files changed (78) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/MachinesModal-LxqWuI5Z.js +1 -0
  3. package/.output/public/assets/{SignIn-0Tjqs-Ma.js → SignIn-Cf4HOpdF.js} +1 -1
  4. package/.output/public/assets/{actionUi-DpwGIGpw.js → actionUi-yYVvoOTn.js} +1 -1
  5. package/.output/public/assets/app-CuZ0I3Mb.css +2 -0
  6. package/.output/public/assets/index-0VPwUcZS.js +10 -0
  7. package/.output/public/assets/{invite._token-Btg_0aLp.js → invite._token-B0EKVyrU.js} +1 -1
  8. package/.output/public/assets/{invite._token-KMD2JAgE.js → invite._token-rUfzvFd9.js} +1 -1
  9. package/.output/public/assets/lifecycleUi-C0DaC6NM.js +1 -0
  10. package/.output/public/assets/loops._loopId-BGXJzKUQ.js +67 -0
  11. package/.output/public/assets/{loops._loopId_.runs._runId-DRCgXaCq.js → loops._loopId_.runs._runId-BL2fgDqc.js} +2 -2
  12. package/.output/public/assets/{preload-helper-DsxECZIS.js → preload-helper-Bs7oCAsw.js} +1 -1
  13. package/.output/public/assets/{routes-LDbnnQ3f.js → routes-4N6mrQK_.js} +1 -1
  14. package/.output/public/assets/routes-BfEn81Yx.js +1 -0
  15. package/.output/public/assets/{t._teamId-DUena1pV.js → t._teamId-CAUjvMZ6.js} +1 -1
  16. package/.output/public/assets/t._teamId-CBTSOgMp.js +1 -0
  17. package/.output/public/assets/{ui-Do4gqMyP.js → ui-s5IMg6RF.js} +1 -1
  18. package/.output/server/_libs/@tanstack/react-router+[...].mjs +1 -1
  19. package/.output/server/_ssr/{DashboardView-C2LdV6J0.mjs → DashboardView-DCxO1HHu.mjs} +14 -93
  20. package/.output/server/_ssr/{MachinesModal-BjkcVjjx.mjs → MachinesModal-CBpDG1jV.mjs} +158 -6
  21. package/.output/server/_ssr/{SignIn-DEGrvJvb.mjs → SignIn-DwHmFBg2.mjs} +3 -3
  22. package/.output/server/_ssr/{SocialLinks-Cm4H8pUJ.mjs → SocialLinks-y7JuqJqp.mjs} +4 -3
  23. package/.output/server/_ssr/{actionUi-DJBWuNwr.mjs → actionUi-DCNmrAjw.mjs} +2 -2
  24. package/.output/server/_ssr/{artifactFiles-DtpRidFZ.mjs → artifactFiles-1f04rZwG.mjs} +3 -3
  25. package/.output/server/_ssr/{artifactKind-FBYbgSuf.mjs → artifactKind-CmNkee8I.mjs} +3 -3
  26. package/.output/server/_ssr/{artifacts-BR3jj0NC.mjs → artifacts-DM3Q8kSN.mjs} +1 -1
  27. package/.output/server/_ssr/{auth-C0zoZCGe.mjs → auth-C_Xnj8JO.mjs} +9 -9
  28. package/.output/server/_ssr/{boot-sNlIkoLe.mjs → boot-D_THchbC.mjs} +16 -2
  29. package/.output/server/_ssr/{boot-CdOO4xOo.mjs → boot-DnVqzrBE.mjs} +1 -1
  30. package/.output/server/_ssr/{bun-sqlite-dialect-BW9W1_Ps-DmlZWqO7.mjs → bun-sqlite-dialect-BW9W1_Ps-mjhr2FKB.mjs} +2 -2
  31. package/.output/server/_ssr/{d1-sqlite-dialect-BLC8LXE6-CCNzqZwZ.mjs → d1-sqlite-dialect-BLC8LXE6-57NKRt7v.mjs} +2 -2
  32. package/.output/server/_ssr/db-BUHGfj7t.mjs +2 -0
  33. package/.output/server/_ssr/{dist-B3SxDOpW.mjs → dist-xlBlQKer.mjs} +2 -2
  34. package/.output/server/_ssr/{factory-Dv3Zopis.mjs → factory-CUICkr5b.mjs} +2 -2
  35. package/.output/server/_ssr/{invite._token-dLKDJKVs.mjs → invite._token-C3ik1Xca.mjs} +6 -6
  36. package/.output/server/_ssr/{invite._token-CJ6-H-s3.mjs → invite._token-CGzJGIfm.mjs} +2 -2
  37. package/.output/server/_ssr/{invite._token-l1SjouDz.mjs → invite._token-CSENXkE5.mjs} +4 -4
  38. package/.output/server/_ssr/{kysely-adapter-nL7WKSgw.mjs → kysely-adapter-8WrOeuSs.mjs} +1 -1
  39. package/.output/server/_ssr/lifecycleUi-DOr6Af13.mjs +51 -0
  40. package/.output/server/_ssr/logger-CQdxLIT9.mjs +2 -0
  41. package/.output/server/_ssr/{loopApi-BKyFB4KM.mjs → loopApi-B7uUJlS3.mjs} +12 -12
  42. package/.output/server/_ssr/{loopProjection-B8a829ij.mjs → loopProjection-B30cdu8n.mjs} +34 -6
  43. package/.output/server/_ssr/{loops._loopId-DS16Phmq.mjs → loops._loopId-3GFocAep.mjs} +61 -105
  44. package/.output/server/_ssr/{loops._loopId_.runs._runId-BEJI6iK5.mjs → loops._loopId_.runs._runId-B2ONGiQe.mjs} +6 -6
  45. package/.output/server/_ssr/{loops._loopId_.runs._runId-CduETLqH.mjs → loops._loopId_.runs._runId-CeJuOVke.mjs} +3 -3
  46. package/.output/server/_ssr/{machineFns-ByMsgG12.mjs → machineFns-DslRixAv.mjs} +3 -3
  47. package/.output/server/_ssr/{node-sqlite-dialect-CjNineno.mjs → node-sqlite-dialect-D74sKAO_.mjs} +2 -2
  48. package/.output/server/_ssr/{router-CXyrnvx-.mjs → router-CAxR0CJG.mjs} +22 -22
  49. package/.output/server/_ssr/{routes-Wjexi35W.mjs → routes-BhM8_kf0.mjs} +5 -5
  50. package/.output/server/_ssr/{routes-DD94baie.mjs → routes-D5GHah06.mjs} +2 -2
  51. package/.output/server/_ssr/{routes-BulNEUEE.mjs → routes-DgjhPWRJ.mjs} +5 -5
  52. package/.output/server/_ssr/runDiff-BIYvi0EH.mjs +2 -0
  53. package/.output/server/_ssr/ssr.mjs +39 -39
  54. package/.output/server/_ssr/store-B_RNzP1r.mjs +2 -0
  55. package/.output/server/_ssr/{t._teamId-COuMKQ4r.mjs → t._teamId-259lg_JB.mjs} +5 -5
  56. package/.output/server/_ssr/{t._teamId-1CreIlPV.mjs → t._teamId-B6d1UVPx.mjs} +5 -5
  57. package/.output/server/_ssr/{t._teamId-Bd6-zZsy.mjs → t._teamId-CrZ7EkUL.mjs} +2 -2
  58. package/.output/server/_ssr/{teamFns-j2JKjorZ.mjs → teamFns-BEvVeIHP.mjs} +2 -2
  59. package/.output/server/_ssr/{teamFns-H438WnZC.mjs → teamFns-C2jguwv8.mjs} +3 -3
  60. package/.output/server/_ssr/tokens-BzwlaFxs.mjs +2 -0
  61. package/.output/server/_ssr/{ui-D5KWstlb.mjs → ui-BEPYdOmJ.mjs} +18 -8
  62. package/.output/server/_tanstack-start-manifest_v-HOdB50PD.mjs +86 -0
  63. package/.output/server/index.mjs +86 -86
  64. package/package.json +1 -1
  65. package/.output/public/assets/MachinesModal-CSbnrUER.js +0 -1
  66. package/.output/public/assets/app-CaZpuwOS.css +0 -2
  67. package/.output/public/assets/index-CaS_SmDq.js +0 -10
  68. package/.output/public/assets/lifecycleUi-C7dwHtbw.js +0 -1
  69. package/.output/public/assets/loops._loopId-D5usF3DO.js +0 -67
  70. package/.output/public/assets/routes-CtFgwdgW.js +0 -1
  71. package/.output/public/assets/t._teamId-D41xcbsF.js +0 -1
  72. package/.output/server/_ssr/db-CwLD-5w1.mjs +0 -2
  73. package/.output/server/_ssr/lifecycleUi-Dad3hvhm.mjs +0 -37
  74. package/.output/server/_ssr/logger-B8m8F1Qu.mjs +0 -2
  75. package/.output/server/_ssr/runDiff-Ba8Lg1vg.mjs +0 -2
  76. package/.output/server/_ssr/store-Cujtg2QE.mjs +0 -2
  77. package/.output/server/_ssr/tokens-DgG_Syq3.mjs +0 -2
  78. package/.output/server/_tanstack-start-manifest_v-3H7yBKLz.mjs +0 -85
@@ -1,7 +1,19 @@
1
- import { G as getMachine, Ht as statusDefinitionsFromLoop, Q as hasRunningRun, Rt as scheduleFromLoop, St as machinePresence, T as daemonNeedsUpdate, Z as hasPendingRun, kt as reconciliationStatesForRuns, mt as listRuns, u as MIN_DAEMON_VERSION, y as countRuns } from "./boot-sNlIkoLe.mjs";
2
- //#region node_modules/.nitro/vite/services/ssr/assets/loopProjection-B8a829ij.js
1
+ import { At as reconciliationStatesForRuns, G as getMachine, Q as hasRunningRun, St as machinePresence, T as daemonNeedsUpdate, Ut as statusDefinitionsFromLoop, Z as hasPendingRun, kt as recentLoopUsage, mt as listRuns, u as MIN_DAEMON_VERSION, y as countRuns, zt as scheduleFromLoop } from "./boot-D_THchbC.mjs";
2
+ //#region node_modules/.nitro/vite/services/ssr/assets/loopProjection-B30cdu8n.js
3
3
  /** Canonical client projections for persisted loops and runs. */
4
4
  var SUMMARY_RUNS = 18;
5
+ var RECENT_USAGE_WINDOW_MS = 10080 * 60 * 1e3;
6
+ /** Dashboard order: loops with runs first, most recent run first. The incoming
7
+ * order remains the tie-breaker, so never-run loops keep listLoops' newest-created
8
+ * ordering and equal timestamps do not make cards jump between polls. */
9
+ function sortLoopSummariesByRecentRun(summaries) {
10
+ return summaries.toSorted((a, b) => {
11
+ if (a.lastRunTs && b.lastRunTs) return b.lastRunTs.localeCompare(a.lastRunTs);
12
+ if (a.lastRunTs) return -1;
13
+ if (b.lastRunTs) return 1;
14
+ return 0;
15
+ });
16
+ }
5
17
  function nextRun(loop) {
6
18
  return [loop.nextRunAt, loop.nextCadenceAt].filter((v) => v != null).sort()[0] ?? null;
7
19
  }
@@ -45,8 +57,10 @@ function toArtifactSummary(row) {
45
57
  oversize: row.oversize
46
58
  };
47
59
  }
48
- async function toLoopSummary(loop) {
60
+ async function toLoopSummaryWithMachine(loop, machine) {
49
61
  const runs = await toRunSummaries(loop.id, await listRuns(loop.id, SUMMARY_RUNS));
62
+ const recentUsage = await recentLoopUsage(loop.id, (/* @__PURE__ */ new Date(Date.now() - RECENT_USAGE_WINDOW_MS)).toISOString());
63
+ const presence = machinePresence(machine?.online, machine?.lastSeen);
50
64
  return {
51
65
  id: loop.id,
52
66
  name: loop.name,
@@ -55,6 +69,13 @@ async function toLoopSummary(loop) {
55
69
  agent: loop.agent,
56
70
  model: loop.model ?? null,
57
71
  reasoningEffort: loop.reasoningEffort ?? null,
72
+ machine: {
73
+ id: loop.machineId,
74
+ name: machine?.name || "",
75
+ online: presence === "online",
76
+ presence,
77
+ lastSeen: machine?.lastSeen ?? null
78
+ },
58
79
  enabled: loop.enabled,
59
80
  nextRun: nextRun(loop),
60
81
  running: await hasRunningRun(loop.id),
@@ -64,9 +85,16 @@ async function toLoopSummary(loop) {
64
85
  deleteRequestedAt: loop.deleteRequestedAt ?? null,
65
86
  pauseCause: loop.pauseCause ?? null,
66
87
  runs,
67
- runCount: await countRuns(loop.id)
88
+ runCount: await countRuns(loop.id),
89
+ recentUsage
68
90
  };
69
91
  }
92
+ /** Batch dashboard projection: machine rows are preloaded once, avoiding a
93
+ * machine lookup per loop on every dashboard poll. */
94
+ async function toLoopSummaries(loops, machines) {
95
+ const byId = new Map(machines.map((machine) => [machine.id, machine]));
96
+ return Promise.all(loops.map((loop) => toLoopSummaryWithMachine(loop, byId.get(loop.machineId))));
97
+ }
70
98
  function toLoopFull(loop) {
71
99
  return {
72
100
  id: loop.id,
@@ -91,7 +119,7 @@ async function toLoopDetail(loop) {
91
119
  const presence = machinePresence(m?.online, m?.lastSeen);
92
120
  return {
93
121
  loop: toLoopFull(loop),
94
- summary: await toLoopSummary(loop),
122
+ summary: await toLoopSummaryWithMachine(loop, m),
95
123
  machine: {
96
124
  id: loop.machineId,
97
125
  name: m?.name || "",
@@ -107,4 +135,4 @@ async function toLoopDetail(loop) {
107
135
  };
108
136
  }
109
137
  //#endregion
110
- export { toRunSummaries as i, toLoopDetail as n, toLoopSummary as r, toArtifactSummary as t };
138
+ export { toRunSummaries as a, toLoopSummaries as i, toArtifactSummary as n, toLoopDetail as r, sortLoopSummariesByRecentRun as t };
@@ -1,18 +1,18 @@
1
1
  import { o as __toESM } from "../_runtime.mjs";
2
- import { s as CODING_AGENTS } from "./boot-sNlIkoLe.mjs";
2
+ import { s as CODING_AGENTS } from "./boot-D_THchbC.mjs";
3
3
  import { g as require_react, h as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
4
- import { C as runPulseStyle, D as until, E as tsShort, T as selectCls, _ as humanBytes, a as btn, c as btnQuiet, d as dotColor, f as dotLabel, h as fmt, m as dur, n as Loading, o as btnDanger, r as Pill, s as btnPrimary, t as ErrorBanner, v as inputCls, w as sectionHeadCls, x as rel, y as labelCls } from "./ui-D5KWstlb.mjs";
5
- import { i as useFlash, n as FlashLine, r as LoadErrorCard, t as ConfirmBar } from "./actionUi-DJBWuNwr.mjs";
6
4
  import { d as useNavigate, u as Link } from "../_libs/@tanstack/react-router+[...].mjs";
7
- import { C as startLoop, E as useSession, _ as loadOlderRuns, b as pauseLoop, c as getArtifact, l as getArtifacts, p as getLoopDetail, s as deleteLoop, w as stopLoop, x as runLoop, y as patchLoop } from "./SocialLinks-Cm4H8pUJ.mjs";
5
+ import { C as startLoop, E as useSession, _ as loadOlderRuns, b as pauseLoop, c as getArtifact, l as getArtifacts, p as getLoopDetail, s as deleteLoop, w as stopLoop, x as runLoop, y as patchLoop } from "./SocialLinks-y7JuqJqp.mjs";
6
+ import { E as tsShort, S as runPulseStyle, T as selectCls, _ as inputCls, a as btn, b as rel, c as btnQuiet, d as dotLabel, g as humanBytes, m as fmt, n as Loading, o as btnDanger, p as dur, r as Pill, s as btnPrimary, t as ErrorBanner, u as dotColor, v as labelCls, w as sectionHeadCls } from "./ui-BEPYdOmJ.mjs";
8
7
  import { t as setActiveTeamCookie } from "./teamCookie-B7hLOIie.mjs";
9
- import { t as SignIn } from "./SignIn-DEGrvJvb.mjs";
10
- import { i as lifecycleDisplay, n as daemonStopSupport, r as deriveLoopLifecycle, t as DAEMON_UPGRADE_REQUIRED } from "./lifecycleUi-Dad3hvhm.mjs";
11
- import { a as Timeline, c as mergeRuns, t as MachinesModal } from "./MachinesModal-BjkcVjjx.mjs";
12
- import { n as artifactKind, t as Route } from "./artifactKind-FBYbgSuf.mjs";
8
+ import { t as SignIn } from "./SignIn-DwHmFBg2.mjs";
9
+ import { i as useFlash, n as FlashLine, r as LoadErrorCard, t as ConfirmBar } from "./actionUi-DCNmrAjw.mjs";
10
+ import { n as daemonStopSupport, r as deriveLoopLifecycle, t as DAEMON_UPGRADE_REQUIRED } from "./lifecycleUi-DOr6Af13.mjs";
11
+ import { l as mergeRuns, n as MachinesModal, o as Timeline, t as LoopOverview } from "./MachinesModal-CBpDG1jV.mjs";
12
+ import { n as artifactKind, t as Route } from "./artifactKind-CmNkee8I.mjs";
13
13
  import { t as purify } from "../_libs/dompurify.mjs";
14
14
  import { t as g } from "../_libs/marked.mjs";
15
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId-DS16Phmq.js
15
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId-3GFocAep.js
16
16
  var import_react = /* @__PURE__ */ __toESM(require_react());
17
17
  var import_jsx_runtime = require_jsx_runtime();
18
18
  /** Download URL for one artifact. Each path segment is encoded independently so
@@ -796,8 +796,7 @@ function LoopDetailView({ id }) {
796
796
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Loading, {})
797
797
  });
798
798
  const { loop, summary, runs } = detail;
799
- const lifecycle = deriveLoopLifecycle(summary);
800
- const deleting = lifecycle === "deleting";
799
+ const deleting = deriveLoopLifecycle(summary) === "deleting";
801
800
  const active = summary.enabled && !deleting;
802
801
  const paused = !summary.enabled && !deleting;
803
802
  const busy = pending != null;
@@ -839,74 +838,41 @@ function LoopDetailView({ id }) {
839
838
  })]
840
839
  })]
841
840
  });
842
- const scheduleText = loop.schedule.mode === "cron" ? `${loop.schedule.cron} · ${loop.schedule.timezone} · overlap ${loop.schedule.overlap}` : `continuous · ${loop.schedule.delayMinutes}m after terminal`;
841
+ const configurationScheduleText = loop.schedule.mode === "cron" ? `${loop.schedule.cron} · ${loop.schedule.timezone} · overlap ${loop.schedule.overlap}` : `continuous · ${loop.schedule.delayMinutes}m after terminal`;
843
842
  const model = loop.model || "default";
844
843
  const effort = loop.reasoningEffort || "default";
844
+ const overviewRuns = older.length ? mergeRuns(summary.runs, older) : summary.runs;
845
+ const loadMore = async () => {
846
+ const oldest = overviewRuns[0];
847
+ if (!oldest) return 0;
848
+ const more = await loadOlderRuns({ data: {
849
+ loopId: id,
850
+ beforeTs: oldest.ts,
851
+ limit: 16
852
+ } });
853
+ if (more.length) setOlder((current) => mergeRuns(current, more));
854
+ return more.length;
855
+ };
845
856
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Shell, {
846
857
  back,
847
858
  children: [
848
859
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
849
- className: "rounded-card border border-hairline bg-surface px-6 pb-5 pt-[22px] shadow-card",
850
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
851
- className: "flex flex-wrap items-start justify-between gap-x-6 gap-y-4",
852
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
853
- className: "min-w-0",
854
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
855
- className: "flex flex-wrap items-center gap-3",
856
- children: [
857
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
858
- className: "text-[28px] font-semibold leading-tight tracking-[-0.015em] text-display",
859
- children: summary.name
860
- }),
861
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
862
- tone: lifecycle === "stopping" ? "running" : void 0,
863
- dot: lifecycle === "stopping" ? "pulse" : void 0,
864
- children: lifecycleDisplay(detail)
865
- }),
866
- summary.queued && lifecycle === "active" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
867
- tone: "outline",
868
- children: "Queued"
869
- }),
870
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
871
- tone: "outline",
872
- children: AGENT_LABEL[loop.agent]
873
- }),
874
- crossTeam && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
875
- tone: "outline",
876
- children: crossTeam.name
877
- })
878
- ]
879
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
880
- className: "mt-2.5 flex flex-wrap items-center gap-x-2.5 gap-y-1 text-meta text-secondary",
881
- children: [
882
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
883
- className: "font-mono text-primary",
884
- children: scheduleText
885
- }),
886
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
887
- className: "text-wire",
888
- children: "·"
889
- }),
890
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: ["next ", fmt(summary.nextRun)] }),
891
- summary.nextRun && summary.enabled && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
892
- className: "text-disabled",
893
- children: [
894
- "(",
895
- until(summary.nextRun),
896
- ")"
897
- ]
898
- }),
899
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
900
- className: "text-wire",
901
- children: "·"
902
- }),
903
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
904
- className: "inline-flex items-center gap-1.5",
905
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `size-1.5 rounded-full ${online ? "bg-rubik-green" : asleep ? "bg-rubik-yellow" : "bg-disabled"}` }), detail.machine.name || "machine"]
906
- })
907
- ]
908
- })]
909
- })
860
+ className: "rounded-card border border-hairline bg-surface px-[26px] pb-5 pt-[22px] shadow-card",
861
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoopOverview, {
862
+ loop: summary,
863
+ runs: overviewRuns,
864
+ onLoadMore: loadMore,
865
+ onPickRun: (run) => navigate({
866
+ to: "/loops/$loopId/runs/$runId",
867
+ params: {
868
+ loopId: id,
869
+ runId: run.id
870
+ }
871
+ }),
872
+ extraPill: crossTeam ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
873
+ tone: "outline",
874
+ children: crossTeam.name
875
+ }) : void 0
910
876
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
911
877
  className: "mt-5 border-t border-hairline pt-4",
912
878
  children: [
@@ -1001,6 +967,27 @@ function LoopDetailView({ id }) {
1001
967
  ]
1002
968
  })]
1003
969
  }),
970
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
971
+ className: "mt-6 grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(300px,360px)]",
972
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoopFilesPanel, {
973
+ loopId: id,
974
+ configuredPaths: loop.artifacts,
975
+ running
976
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunsSection, {
977
+ loopId: id,
978
+ summary,
979
+ runs,
980
+ older,
981
+ onPickRun: (run) => navigate({
982
+ to: "/loops/$loopId/runs/$runId",
983
+ params: {
984
+ loopId: id,
985
+ runId: run.id
986
+ }
987
+ }),
988
+ onMore: loadMore
989
+ })]
990
+ }),
1004
991
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
1005
992
  className: "mt-6 min-w-0 rounded-card border border-hairline bg-surface px-6 py-5 shadow-card",
1006
993
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
@@ -1013,7 +1000,7 @@ function LoopDetailView({ id }) {
1013
1000
  label: "Schedule",
1014
1001
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", {
1015
1002
  className: "break-all font-mono",
1016
- children: scheduleText
1003
+ children: configurationScheduleText
1017
1004
  })
1018
1005
  }),
1019
1006
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Config, {
@@ -1081,37 +1068,6 @@ function LoopDetailView({ id }) {
1081
1068
  ]
1082
1069
  })]
1083
1070
  }),
1084
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1085
- className: "mt-6 grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(300px,360px)]",
1086
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoopFilesPanel, {
1087
- loopId: id,
1088
- configuredPaths: loop.artifacts,
1089
- running
1090
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunsSection, {
1091
- loopId: id,
1092
- summary,
1093
- runs,
1094
- older,
1095
- onPickRun: (run) => navigate({
1096
- to: "/loops/$loopId/runs/$runId",
1097
- params: {
1098
- loopId: id,
1099
- runId: run.id
1100
- }
1101
- }),
1102
- onMore: async () => {
1103
- const oldest = (older.length ? mergeRuns(summary.runs, older) : summary.runs)[0];
1104
- if (!oldest) return 0;
1105
- const more = await loadOlderRuns({ data: {
1106
- loopId: id,
1107
- beforeTs: oldest.ts,
1108
- limit: 16
1109
- } });
1110
- if (more.length) setOlder((current) => mergeRuns(current, more));
1111
- return more.length;
1112
- }
1113
- })]
1114
- }),
1115
1071
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MachinesModal, {
1116
1072
  open: machinesOpen,
1117
1073
  onClose: () => setMachinesOpen(false)
@@ -1,12 +1,12 @@
1
1
  import { o as __toESM } from "../_runtime.mjs";
2
2
  import { g as require_react, h as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
3
- import { C as runPulseStyle, _ as humanBytes, a as btn, g as fnum, h as fmt, i as StatusPill, m as dur, n as Loading, o as btnDanger, r as Pill, w as sectionHeadCls } from "./ui-D5KWstlb.mjs";
4
- import { r as LoadErrorCard } from "./actionUi-DJBWuNwr.mjs";
5
3
  import { u as Link } from "../_libs/@tanstack/react-router+[...].mjs";
6
- import { E as useSession, T as stopRun, _ as loadOlderRuns, m as getRunDiff, p as getLoopDetail } from "./SocialLinks-Cm4H8pUJ.mjs";
7
- import { t as SignIn } from "./SignIn-DEGrvJvb.mjs";
8
- import { t as Route } from "./loops._loopId_.runs._runId-CduETLqH.mjs";
9
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-BEJI6iK5.js
4
+ import { E as useSession, T as stopRun, _ as loadOlderRuns, m as getRunDiff, p as getLoopDetail } from "./SocialLinks-y7JuqJqp.mjs";
5
+ import { S as runPulseStyle, a as btn, g as humanBytes, h as fnum, i as StatusPill, m as fmt, n as Loading, o as btnDanger, p as dur, r as Pill, w as sectionHeadCls } from "./ui-BEPYdOmJ.mjs";
6
+ import { t as SignIn } from "./SignIn-DwHmFBg2.mjs";
7
+ import { r as LoadErrorCard } from "./actionUi-DCNmrAjw.mjs";
8
+ import { t as Route } from "./loops._loopId_.runs._runId-CeJuOVke.mjs";
9
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-B2ONGiQe.js
10
10
  var import_react = /* @__PURE__ */ __toESM(require_react());
11
11
  var import_jsx_runtime = require_jsx_runtime();
12
12
  /**
@@ -1,7 +1,7 @@
1
1
  import { c as createFileRoute, s as lazyRouteComponent } from "../_libs/@tanstack/react-router+[...].mjs";
2
- import { i as authClient, u as getAuthState } from "./SocialLinks-Cm4H8pUJ.mjs";
3
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-CduETLqH.js
4
- var $$splitComponentImporter = () => import("./loops._loopId_.runs._runId-BEJI6iK5.mjs");
2
+ import { i as authClient, u as getAuthState } from "./SocialLinks-y7JuqJqp.mjs";
3
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-CeJuOVke.js
4
+ var $$splitComponentImporter = () => import("./loops._loopId_.runs._runId-B2ONGiQe.mjs");
5
5
  /**
6
6
  * Run detail PAGE — `/loops/$loopId/runs/$runId`. A standalone page (the trailing
7
7
  * `_` on the `$loopId` segment opts it out of the loop page's component so the run
@@ -1,8 +1,8 @@
1
- import { Bt as sha256, C as createMachine$1, Ct as mintDeviceToken, G as getMachine, Jt as updateMachine, T as daemonNeedsUpdate, bt as loopsForMachine, dt as listMachines$1, ft as listMachinesForTeam, j as ensureServer, jt as rememberConnectKey, k as deleteMachine$1, u as MIN_DAEMON_VERSION, xt as machineIdFromToken } from "./boot-sNlIkoLe.mjs";
2
- import { requestScope } from "./auth-C0zoZCGe.mjs";
1
+ import { C as createMachine$1, Ct as mintDeviceToken, G as getMachine, Mt as rememberConnectKey, T as daemonNeedsUpdate, Vt as sha256, Yt as updateMachine, bt as loopsForMachine, dt as listMachines$1, ft as listMachinesForTeam, j as ensureServer, k as deleteMachine$1, u as MIN_DAEMON_VERSION, xt as machineIdFromToken } from "./boot-D_THchbC.mjs";
2
+ import { requestScope } from "./auth-C_Xnj8JO.mjs";
3
3
  import { n as createServerFn } from "./ssr.mjs";
4
4
  import { t as createServerRpc } from "./createServerRpc-BD8Xn_0_.mjs";
5
- //#region node_modules/.nitro/vite/services/ssr/assets/machineFns-ByMsgG12.js
5
+ //#region node_modules/.nitro/vite/services/ssr/assets/machineFns-DslRixAv.js
6
6
  /**
7
7
  * Whether a machine is visible/actionable in the request scope. Mirrors
8
8
  * listMachines: open mode sees everything; otherwise the requester must own the
@@ -1,6 +1,6 @@
1
- import { n as DefaultQueryCompiler, o as CompiledQuery, s as sql$1 } from "./auth-C0zoZCGe.mjs";
1
+ import { n as DefaultQueryCompiler, o as CompiledQuery, s as sql$1 } from "./auth-C_Xnj8JO.mjs";
2
2
  import { n as DEFAULT_MIGRATION_TABLE, t as DEFAULT_MIGRATION_LOCK_TABLE } from "./kysely-migration-tables-JkVUjPF_-bPAX9LR_.mjs";
3
- //#region node_modules/.nitro/vite/services/ssr/assets/node-sqlite-dialect-CjNineno.js
3
+ //#region node_modules/.nitro/vite/services/ssr/assets/node-sqlite-dialect-D74sKAO_.js
4
4
  var NodeSqliteAdapter = class {
5
5
  get supportsCreateIfNotExists() {
6
6
  return true;
@@ -1,15 +1,15 @@
1
1
  import { j as sql } from "../_libs/drizzle-orm+postgres.mjs";
2
- import { Dt as readByteBody, Ot as readJsonBody, Qt as validPollWireId, Xt as validOptionalPollString, Zt as validPollCurrentRun, d as POLL_HTTP_BODY_FIELDS, j as ensureServer, l as MACHINE_BODY_CAP } from "./boot-sNlIkoLe.mjs";
2
+ import { $t as validPollWireId, Dt as readByteBody, Ot as readJsonBody, Qt as validPollCurrentRun, Zt as validOptionalPollString, d as POLL_HTTP_BODY_FIELDS, j as ensureServer, l as MACHINE_BODY_CAP } from "./boot-D_THchbC.mjs";
3
3
  import { h as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
4
4
  import { c as createFileRoute, i as HeadContent, l as createRootRoute, o as createRouter, r as Scripts } from "../_libs/@tanstack/react-router+[...].mjs";
5
- import { t as Route$12 } from "./invite._token-l1SjouDz.mjs";
6
- import { r as imageMime, t as Route$13 } from "./artifactKind-FBYbgSuf.mjs";
7
- import { t as Route$14 } from "./loops._loopId_.runs._runId-CduETLqH.mjs";
8
- import { t as Route$15 } from "./routes-BulNEUEE.mjs";
9
- import { t as Route$16 } from "./t._teamId-1CreIlPV.mjs";
10
- //#region node_modules/.nitro/vite/services/ssr/assets/router-CXyrnvx-.js
5
+ import { t as Route$12 } from "./invite._token-CSENXkE5.mjs";
6
+ import { r as imageMime, t as Route$13 } from "./artifactKind-CmNkee8I.mjs";
7
+ import { t as Route$14 } from "./loops._loopId_.runs._runId-CeJuOVke.mjs";
8
+ import { t as Route$15 } from "./routes-DgjhPWRJ.mjs";
9
+ import { t as Route$16 } from "./t._teamId-B6d1UVPx.mjs";
10
+ //#region node_modules/.nitro/vite/services/ssr/assets/router-CAxR0CJG.js
11
11
  var import_jsx_runtime = require_jsx_runtime();
12
- var app_default = "/assets/app-CaZpuwOS.css";
12
+ var app_default = "/assets/app-CuZ0I3Mb.css";
13
13
  var Route$11 = createRootRoute({
14
14
  head: () => ({
15
15
  meta: [
@@ -173,7 +173,7 @@ var Route$10 = createFileRoute("/machine/report")({ server: { handlers: { POST:
173
173
  const parsed = await readJsonBody(request, MACHINE_BODY_CAP);
174
174
  if (parsed.kind === "too-large") return Response.json({ error: "body too large" }, { status: 413 });
175
175
  const body = parsed.kind === "ok" && parsed.body && typeof parsed.body === "object" ? parsed.body : {};
176
- const { getGateway } = await import("./boot-CdOO4xOo.mjs");
176
+ const { getGateway } = await import("./boot-DnVqzrBE.mjs");
177
177
  const r = await (await getGateway()).report(token, body);
178
178
  return Response.json(r.body, { status: r.status });
179
179
  } } } });
@@ -213,13 +213,13 @@ var Route$7 = createFileRoute("/api/machine/sync")({ server: { handlers: { POST:
213
213
  const auth = request.headers.get("authorization") ?? "";
214
214
  const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
215
215
  if (!token) return Response.json({ error: "missing device token" }, { status: 401 });
216
- const { SYNC_BODY_CAP, parseArtifactSyncBody } = await import("./artifacts-BR3jj0NC.mjs");
216
+ const { SYNC_BODY_CAP, parseArtifactSyncBody } = await import("./artifacts-DM3Q8kSN.mjs");
217
217
  const parsed = await readJsonBody(request, SYNC_BODY_CAP);
218
218
  if (parsed.kind === "too-large") return Response.json({ error: "sync body too large" }, { status: 413 });
219
219
  if (parsed.kind === "invalid") return Response.json({ error: "invalid JSON" }, { status: 400 });
220
220
  const body = parseArtifactSyncBody(parsed.body);
221
221
  if (!body) return Response.json({ error: "invalid sync body" }, { status: 400 });
222
- const { getArtifactSync } = await import("./boot-CdOO4xOo.mjs");
222
+ const { getArtifactSync } = await import("./boot-DnVqzrBE.mjs");
223
223
  const r = await (await getArtifactSync()).sync(token, body);
224
224
  return Response.json(r.body, { status: r.status });
225
225
  } } } });
@@ -230,7 +230,7 @@ var Route$6 = createFileRoute("/api/machine/status")({ server: { handlers: { GET
230
230
  const limited = machineRouteLimit(request, token || void 0);
231
231
  if (limited) return limited;
232
232
  if (!token) return Response.json({ error: "missing device token" }, { status: 401 });
233
- const { getGateway } = await import("./boot-CdOO4xOo.mjs");
233
+ const { getGateway } = await import("./boot-DnVqzrBE.mjs");
234
234
  const r = await (await getGateway()).status(token);
235
235
  return Response.json(r.body, { status: r.status });
236
236
  } } } });
@@ -274,7 +274,7 @@ var Route$5 = createFileRoute("/api/machine/poll")({ server: { handlers: { POST:
274
274
  if (parsed.kind !== "ok") return Response.json({ error: "invalid JSON body" }, { status: 400 });
275
275
  const body = parsePollBody(parsed.body);
276
276
  if (!body) return Response.json({ error: "unknown or invalid poll body fields" }, { status: 400 });
277
- const { getGateway } = await import("./boot-CdOO4xOo.mjs");
277
+ const { getGateway } = await import("./boot-DnVqzrBE.mjs");
278
278
  const r = await (await getGateway()).pollV4Wait(token, gatewayPollRequest(body));
279
279
  return Response.json(r.body, { status: r.status });
280
280
  } } } });
@@ -301,7 +301,7 @@ var Route$4 = createFileRoute("/api/machine/cli")({ server: { handlers: { POST:
301
301
  if (parsed.kind === "too-large") return Response.json({ error: "body too large" }, { status: 413 });
302
302
  const body = parsed.kind === "ok" ? parseCliBody(parsed.body) : null;
303
303
  if (!body) return Response.json({ error: "body must be exactly {argv:string[]}" }, { status: 400 });
304
- const { getCliGateway } = await import("./boot-CdOO4xOo.mjs");
304
+ const { getCliGateway } = await import("./boot-DnVqzrBE.mjs");
305
305
  const r = await (await getCliGateway()).cli(token, body.argv);
306
306
  return Response.json(r.body, { status: r.status });
307
307
  } } } });
@@ -330,7 +330,7 @@ var Route$4 = createFileRoute("/api/machine/cli")({ server: { handlers: { POST:
330
330
  var PROBE_TIMEOUT_MS = 5e3;
331
331
  var Route$3 = createFileRoute("/api/health/db")({ server: { handlers: { GET: async () => {
332
332
  try {
333
- const { db } = await import("./db-CwLD-5w1.mjs");
333
+ const { db } = await import("./db-BUHGfj7t.mjs");
334
334
  const ping = db.execute(sql`select 1`);
335
335
  const timeout = new Promise((_, reject) => {
336
336
  setTimeout(() => reject(/* @__PURE__ */ new Error(`db ping timed out after ${PROBE_TIMEOUT_MS}ms`)), PROBE_TIMEOUT_MS).unref?.();
@@ -350,8 +350,8 @@ var Route$3 = createFileRoute("/api/health/db")({ server: { handlers: { GET: asy
350
350
  } } } });
351
351
  /** Better Auth handler — GET/POST /api/auth/* (OAuth callbacks, session, sign-out). */
352
352
  var Route$2 = createFileRoute("/api/auth/$")({ server: { handlers: {
353
- GET: async ({ request }) => (await import("./auth-C0zoZCGe.mjs")).auth.handler(request),
354
- POST: async ({ request }) => (await import("./auth-C0zoZCGe.mjs")).auth.handler(request)
353
+ GET: async ({ request }) => (await import("./auth-C_Xnj8JO.mjs")).auth.handler(request),
354
+ POST: async ({ request }) => (await import("./auth-C_Xnj8JO.mjs")).auth.handler(request)
355
355
  } } });
356
356
  /**
357
357
  * `decodeURIComponent` that returns null on malformed percent-encoding instead
@@ -385,12 +385,12 @@ var Route$1 = createFileRoute("/api/machine/blob/$hash")({ server: { handlers: {
385
385
  if (!token) return Response.json({ error: "missing device token" }, { status: 401 });
386
386
  const hash = safeDecode(new URL(request.url).pathname.split("/").pop() ?? "");
387
387
  if (hash === null) return Response.json({ error: "bad hash" }, { status: 400 });
388
- const { BLOB_CAP } = await import("./artifacts-BR3jj0NC.mjs");
388
+ const { BLOB_CAP } = await import("./artifacts-DM3Q8kSN.mjs");
389
389
  const body = await readByteBody(request, BLOB_CAP);
390
390
  if (body.kind === "too-large") return Response.json({ error: "blob exceeds size cap" }, { status: 413 });
391
391
  if (body.kind === "invalid") return Response.json({ error: "could not read blob body" }, { status: 400 });
392
392
  const buf = Buffer.from(body.bytes.buffer, body.bytes.byteOffset, body.bytes.byteLength);
393
- const { getArtifactSync } = await import("./boot-CdOO4xOo.mjs");
393
+ const { getArtifactSync } = await import("./boot-DnVqzrBE.mjs");
394
394
  const r = await (await getArtifactSync()).putBlob(token, hash, buf);
395
395
  return Response.json(r.body, { status: r.status });
396
396
  } } } });
@@ -425,11 +425,11 @@ var Route = createFileRoute("/api/artifact/$loopId/$")({ server: { handlers: { G
425
425
  const segments = after.slice(slash + 1).split("/").map((s) => safeDecode(s));
426
426
  if (segments.some((s) => s === null)) return Response.json({ error: "bad path" }, { status: 400 });
427
427
  const relPath = segments.join("/");
428
- const loop = await (await import("./store-Cujtg2QE.mjs")).getLoop(loopId);
428
+ const loop = await (await import("./store-B_RNzP1r.mjs")).getLoop(loopId);
429
429
  if (!loop) return Response.json({ error: "not found" }, { status: 404 });
430
- const { requestScope, canAccessLoop } = await import("./auth-C0zoZCGe.mjs");
430
+ const { requestScope, canAccessLoop } = await import("./auth-C_Xnj8JO.mjs");
431
431
  if (!await canAccessLoop(loop.teamId, await requestScope())) return Response.json({ error: "not found" }, { status: 404 });
432
- const { readLoopArtifactBytes } = await import("./artifactFiles-DtpRidFZ.mjs");
432
+ const { readLoopArtifactBytes } = await import("./artifactFiles-1f04rZwG.mjs");
433
433
  const r = await readLoopArtifactBytes(loopId, relPath);
434
434
  if (r.status !== 200 || !r.bytes) return Response.json({ error: "not found" }, { status: r.status });
435
435
  const filename = (r.filename || "file").replace(/["\\\r\n]/g, "_");
@@ -1,9 +1,9 @@
1
1
  import { h as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
2
- import { E as useSession } from "./SocialLinks-Cm4H8pUJ.mjs";
3
- import { t as SignIn } from "./SignIn-DEGrvJvb.mjs";
4
- import { t as DashboardView } from "./DashboardView-C2LdV6J0.mjs";
5
- import { t as Route } from "./routes-BulNEUEE.mjs";
6
- //#region node_modules/.nitro/vite/services/ssr/assets/routes-Wjexi35W.js
2
+ import { E as useSession } from "./SocialLinks-y7JuqJqp.mjs";
3
+ import { t as SignIn } from "./SignIn-DwHmFBg2.mjs";
4
+ import { t as DashboardView } from "./DashboardView-DCxO1HHu.mjs";
5
+ import { t as Route } from "./routes-DgjhPWRJ.mjs";
6
+ //#region node_modules/.nitro/vite/services/ssr/assets/routes-BhM8_kf0.js
7
7
  var import_jsx_runtime = require_jsx_runtime();
8
8
  /**
9
9
  * The home route. Under the auth gate it is now a THIN redirect to the explicit
@@ -1,7 +1,7 @@
1
1
  import { h as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
2
- import { r as LoadErrorCard } from "./actionUi-DJBWuNwr.mjs";
3
2
  import { f as useRouter } from "../_libs/@tanstack/react-router+[...].mjs";
4
- //#region node_modules/.nitro/vite/services/ssr/assets/routes-DD94baie.js
3
+ import { r as LoadErrorCard } from "./actionUi-DCNmrAjw.mjs";
4
+ //#region node_modules/.nitro/vite/services/ssr/assets/routes-D5GHah06.js
5
5
  var import_jsx_runtime = require_jsx_runtime();
6
6
  /**
7
7
  * The home route. Under the auth gate it is now a THIN redirect to the explicit
@@ -1,9 +1,9 @@
1
1
  import { c as createFileRoute, p as redirect, s as lazyRouteComponent } from "../_libs/@tanstack/react-router+[...].mjs";
2
- import { f as getDefaultTeam, i as authClient, u as getAuthState } from "./SocialLinks-Cm4H8pUJ.mjs";
3
- import { n as fetchLiveData } from "./DashboardView-C2LdV6J0.mjs";
4
- //#region node_modules/.nitro/vite/services/ssr/assets/routes-BulNEUEE.js
5
- var $$splitErrorComponentImporter = () => import("./routes-DD94baie.mjs");
6
- var $$splitComponentImporter = () => import("./routes-Wjexi35W.mjs");
2
+ import { f as getDefaultTeam, i as authClient, u as getAuthState } from "./SocialLinks-y7JuqJqp.mjs";
3
+ import { n as fetchLiveData } from "./DashboardView-DCxO1HHu.mjs";
4
+ //#region node_modules/.nitro/vite/services/ssr/assets/routes-DgjhPWRJ.js
5
+ var $$splitErrorComponentImporter = () => import("./routes-D5GHah06.mjs");
6
+ var $$splitComponentImporter = () => import("./routes-BhM8_kf0.mjs");
7
7
  /**
8
8
  * The home route. Under the auth gate it is now a THIN redirect to the explicit
9
9
  * team URL (`/t/<lastUsed|personal>`) so the dashboard's team lives in the path
@@ -0,0 +1,2 @@
1
+ import { _ as computeRunDiff } from "./boot-D_THchbC.mjs";
2
+ export { computeRunDiff };