@kky42/pievo-server 2.3.0 → 2.4.2

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 (199) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/MachinesModal-CSbnrUER.js +1 -0
  3. package/.output/public/assets/SignIn-0Tjqs-Ma.js +1 -0
  4. package/.output/public/assets/actionUi-DpwGIGpw.js +1 -0
  5. package/.output/public/assets/app-CaZpuwOS.css +2 -0
  6. package/.output/public/assets/index-CaS_SmDq.js +10 -0
  7. package/.output/public/assets/invite._token-Btg_0aLp.js +1 -0
  8. package/.output/public/assets/invite._token-KMD2JAgE.js +1 -0
  9. package/.output/public/assets/lifecycleUi-C7dwHtbw.js +1 -0
  10. package/.output/public/assets/loops._loopId-D5usF3DO.js +67 -0
  11. package/.output/public/assets/loops._loopId_.runs._runId-DRCgXaCq.js +3 -0
  12. package/.output/public/assets/preload-helper-DsxECZIS.js +5 -0
  13. package/.output/public/assets/routes-CtFgwdgW.js +1 -0
  14. package/.output/public/assets/routes-LDbnnQ3f.js +1 -0
  15. package/.output/public/assets/t._teamId-D41xcbsF.js +1 -0
  16. package/.output/public/assets/t._teamId-DUena1pV.js +1 -0
  17. package/.output/public/assets/ui-Do4gqMyP.js +1 -0
  18. package/.output/server/_libs/@base-ui/react+[...].mjs +4130 -4130
  19. package/.output/server/_libs/@tanstack/react-router+[...].mjs +1 -1
  20. package/.output/server/_libs/drizzle-orm+postgres.mjs +28 -1
  21. package/.output/server/_ssr/DashboardView-C2LdV6J0.mjs +914 -0
  22. package/.output/server/_ssr/{notifyFns-DuPR1zd7.mjs → MachinesModal-BjkcVjjx.mjs} +55 -35
  23. package/.output/server/_ssr/{SignIn-RmpWm9uy.mjs → SignIn-DEGrvJvb.mjs} +10 -24
  24. package/.output/server/_ssr/SocialLinks-Cm4H8pUJ.mjs +1044 -0
  25. package/.output/server/_ssr/{actionUi-BN55TNYE.mjs → actionUi-DJBWuNwr.mjs} +3 -3
  26. package/.output/server/_ssr/{artifactFiles-BCaIIrrT.mjs → artifactFiles-DtpRidFZ.mjs} +7 -8
  27. package/.output/server/_ssr/{artifactKind-BW0isV8L.mjs → artifactKind-FBYbgSuf.mjs} +27 -2
  28. package/.output/server/_ssr/artifacts-BR3jj0NC.mjs +2 -0
  29. package/.output/server/_ssr/{auth-30Z4lV1w.mjs → auth-C0zoZCGe.mjs} +16 -19
  30. package/.output/server/_ssr/boot-CdOO4xOo.mjs +2 -0
  31. package/.output/server/_ssr/{boot-3BNKujyp.mjs → boot-sNlIkoLe.mjs} +1331 -3927
  32. package/.output/server/_ssr/{bun-sqlite-dialect-BW9W1_Ps-_7tfuNRi.mjs → bun-sqlite-dialect-BW9W1_Ps-DmlZWqO7.mjs} +2 -2
  33. package/.output/server/_ssr/{d1-sqlite-dialect-BLC8LXE6-ucDUbeBk.mjs → d1-sqlite-dialect-BLC8LXE6-CCNzqZwZ.mjs} +2 -2
  34. package/.output/server/_ssr/db-CwLD-5w1.mjs +2 -0
  35. package/.output/server/_ssr/{dist-Cd6nlLlO.mjs → dist-B3SxDOpW.mjs} +2 -2
  36. package/.output/server/_ssr/{factory-CWWwV7XZ.mjs → factory-Dv3Zopis.mjs} +2 -2
  37. package/.output/server/_ssr/invite._token-CJ6-H-s3.mjs +20 -0
  38. package/.output/server/_ssr/{invite._token-SdWXemIc.mjs → invite._token-dLKDJKVs.mjs} +9 -17
  39. package/.output/server/_ssr/{invite._token-BKlz0R2f.mjs → invite._token-l1SjouDz.mjs} +6 -15
  40. package/.output/server/_ssr/{kysely-adapter-C8iS51DB.mjs → kysely-adapter-nL7WKSgw.mjs} +1 -1
  41. package/.output/server/_ssr/{lifecycleUi-DgBQAYzp.mjs → lifecycleUi-Dad3hvhm.mjs} +11 -11
  42. package/.output/server/_ssr/logger-B8m8F1Qu.mjs +2 -0
  43. package/.output/server/_ssr/loopApi-BKyFB4KM.mjs +342 -0
  44. package/.output/server/_ssr/{adapters-BoLkOf1T.mjs → loopProjection-B8a829ij.mjs} +25 -55
  45. package/.output/server/_ssr/loops._loopId-DS16Phmq.mjs +1271 -0
  46. package/.output/server/_ssr/{loops._loopId_.runs._runId-PUGBrD5q.mjs → loops._loopId_.runs._runId-BEJI6iK5.mjs} +23 -74
  47. package/.output/server/_ssr/{loops._loopId_.runs._runId-Ymy4lwee.mjs → loops._loopId_.runs._runId-CduETLqH.mjs} +4 -5
  48. package/.output/server/_ssr/{machineFns-BZ9puxDt.mjs → machineFns-ByMsgG12.mjs} +3 -5
  49. package/.output/server/_ssr/{node-sqlite-dialect-D_3JmkMo.mjs → node-sqlite-dialect-CjNineno.mjs} +2 -2
  50. package/.output/server/_ssr/router-CXyrnvx-.mjs +565 -0
  51. package/.output/server/_ssr/{routes-ClZEdsnB.mjs → routes-BulNEUEE.mjs} +6 -10
  52. package/.output/server/_ssr/{routes-ComqHvCL.mjs → routes-DD94baie.mjs} +3 -3
  53. package/.output/server/_ssr/{routes-D5jJJ-30.mjs → routes-Wjexi35W.mjs} +6 -6
  54. package/.output/server/_ssr/runDiff-Ba8Lg1vg.mjs +2 -0
  55. package/.output/server/_ssr/ssr.mjs +60 -100
  56. package/.output/server/_ssr/store-Cujtg2QE.mjs +2 -0
  57. package/.output/server/_ssr/{t._teamId-BgHV_KFP.mjs → t._teamId-1CreIlPV.mjs} +7 -11
  58. package/.output/server/_ssr/{t._teamId-DDERNgfn.mjs → t._teamId-Bd6-zZsy.mjs} +4 -4
  59. package/.output/server/_ssr/{t._teamId-DRJvKHZn.mjs → t._teamId-COuMKQ4r.mjs} +7 -7
  60. package/.output/server/_ssr/{teamFns-Bp81UO-x.mjs → teamFns-H438WnZC.mjs} +12 -30
  61. package/.output/server/_ssr/{teamFns-bm82twd5.mjs → teamFns-j2JKjorZ.mjs} +2 -2
  62. package/.output/server/_ssr/tokens-DgG_Syq3.mjs +2 -0
  63. package/.output/server/_ssr/{ui-Ww2tTM2u.mjs → ui-D5KWstlb.mjs} +26 -48
  64. package/.output/server/{_tanstack-start-manifest_v-DwKENYCN.mjs → _tanstack-start-manifest_v-3H7yBKLz.mjs} +25 -35
  65. package/{drizzle/0000_round_marauders.sql → .output/server/drizzle/0000_baseline.sql} +102 -44
  66. package/.output/server/drizzle/meta/_journal.json +3 -115
  67. package/.output/server/index.mjs +83 -132
  68. package/{.output/server/drizzle/0000_round_marauders.sql → drizzle/0000_baseline.sql} +102 -44
  69. package/drizzle/meta/_journal.json +3 -115
  70. package/package.json +2 -11
  71. package/scripts/pievo-server.mjs +3 -1
  72. package/scripts/server-cli-lib.mjs +23 -16
  73. package/.output/public/assets/CodeField-dOzyFQNH.js +0 -34
  74. package/.output/public/assets/LoopView-DjVHUyrR.js +0 -59
  75. package/.output/public/assets/Modal-CCRvx959.js +0 -1
  76. package/.output/public/assets/SignIn-Qxw5zDFq.js +0 -1
  77. package/.output/public/assets/actionUi-CXxhzYe2.js +0 -1
  78. package/.output/public/assets/app-D1cij_NJ.css +0 -2
  79. package/.output/public/assets/artifactView-Cd5wWPil.js +0 -66
  80. package/.output/public/assets/index-BRN_CQ8U.js +0 -12
  81. package/.output/public/assets/invite._token-BIxr8DlP.js +0 -1
  82. package/.output/public/assets/invite._token-j2BTCeG1.js +0 -1
  83. package/.output/public/assets/jsx-runtime-BseJUIpC.js +0 -1
  84. package/.output/public/assets/lifecycleUi-Qy_8beQK.js +0 -1
  85. package/.output/public/assets/loopApi-CT1mMhlb.js +0 -3
  86. package/.output/public/assets/loops._loopId-BRBQzioy.js +0 -4
  87. package/.output/public/assets/loops._loopId_.runs._runId-CMnOJkem.js +0 -3
  88. package/.output/public/assets/notifyFns-DYD4EKaG.js +0 -1
  89. package/.output/public/assets/preload-helper-DFkkpP6W.js +0 -3
  90. package/.output/public/assets/routes-3nuEJGdo.js +0 -1
  91. package/.output/public/assets/routes-DORLim-C.js +0 -1
  92. package/.output/public/assets/t._teamId-F5f4K8kd.js +0 -1
  93. package/.output/public/assets/t._teamId-XfE_S1Fo.js +0 -1
  94. package/.output/public/assets/ui-Dhsx6SW0.js +0 -1
  95. package/.output/public/assets/useRouter-DsjH8G0p.js +0 -1
  96. package/.output/server/_libs/@codemirror/autocomplete+[...].mjs +0 -19713
  97. package/.output/server/_libs/@codemirror/lang-css+[...].mjs +0 -2636
  98. package/.output/server/_libs/@codemirror/lang-html+[...].mjs +0 -2247
  99. package/.output/server/_libs/@codemirror/lang-json+[...].mjs +0 -79
  100. package/.output/server/_libs/@reduxjs/toolkit+[...].mjs +0 -1736
  101. package/.output/server/_libs/@uiw/codemirror-extensions-basic-setup+[...].mjs +0 -76
  102. package/.output/server/_libs/babel__runtime.mjs +0 -30
  103. package/.output/server/_libs/clsx.mjs +0 -16
  104. package/.output/server/_libs/codemirror__commands.mjs +0 -1725
  105. package/.output/server/_libs/codemirror__lint.mjs +0 -565
  106. package/.output/server/_libs/codemirror__search.mjs +0 -1138
  107. package/.output/server/_libs/codemirror__theme-one-dark.mjs +0 -161
  108. package/.output/server/_libs/d3-array.mjs +0 -231
  109. package/.output/server/_libs/d3-color.mjs +0 -330
  110. package/.output/server/_libs/d3-format.mjs +0 -252
  111. package/.output/server/_libs/d3-interpolate.mjs +0 -163
  112. package/.output/server/_libs/d3-path.mjs +0 -77
  113. package/.output/server/_libs/d3-scale+[...].mjs +0 -1907
  114. package/.output/server/_libs/d3-shape.mjs +0 -818
  115. package/.output/server/_libs/decimal.js-light.mjs +0 -884
  116. package/.output/server/_libs/dom-serializer+[...].mjs +0 -708
  117. package/.output/server/_libs/domhandler.mjs +0 -461
  118. package/.output/server/_libs/domutils.mjs +0 -868
  119. package/.output/server/_libs/es-toolkit.mjs +0 -1737
  120. package/.output/server/_libs/eventemitter3.mjs +0 -236
  121. package/.output/server/_libs/html-dom-parser+htmlparser2.mjs +0 -1560
  122. package/.output/server/_libs/html-react-parser+[...].mjs +0 -1360
  123. package/.output/server/_libs/react-is.mjs +0 -48
  124. package/.output/server/_libs/react-redux+[...].mjs +0 -205
  125. package/.output/server/_libs/recharts+victory-vendor.mjs +0 -17303
  126. package/.output/server/_libs/uiw__codemirror-themes.mjs +0 -32
  127. package/.output/server/_libs/uiw__react-codemirror.mjs +0 -354
  128. package/.output/server/_ssr/CodeField-B7oOmmwH.mjs +0 -143
  129. package/.output/server/_ssr/DashboardView-BF7PciP7.mjs +0 -2736
  130. package/.output/server/_ssr/LoopPlaybook-DC7QVvPY.mjs +0 -2105
  131. package/.output/server/_ssr/LoopView-DwSrm9Ja.mjs +0 -1300
  132. package/.output/server/_ssr/Modal-MaCG52Pv.mjs +0 -49
  133. package/.output/server/_ssr/artifactView-iuPsp5aa.mjs +0 -304
  134. package/.output/server/_ssr/artifacts-jQ-bfdhI.mjs +0 -2
  135. package/.output/server/_ssr/boot-CsEFT53K.mjs +0 -2
  136. package/.output/server/_ssr/db-CXownd1H.mjs +0 -2
  137. package/.output/server/_ssr/invite._token-CWl6kipD.mjs +0 -28
  138. package/.output/server/_ssr/logger-Gy-47z3k.mjs +0 -2
  139. package/.output/server/_ssr/loopApi-8_KgBapr.mjs +0 -109
  140. package/.output/server/_ssr/loopApi-ABi9SgWS.mjs +0 -564
  141. package/.output/server/_ssr/loops._loopId-SON_t43g.mjs +0 -30
  142. package/.output/server/_ssr/loops._loopId-p1KWsvMM.mjs +0 -1477
  143. package/.output/server/_ssr/notifyFns-BUCePuF3.mjs +0 -118
  144. package/.output/server/_ssr/router-Czxvra6l.mjs +0 -685
  145. package/.output/server/_ssr/runDiff--qLtU4FI.mjs +0 -2
  146. package/.output/server/_ssr/store-40eVzW1S.mjs +0 -2
  147. package/.output/server/_ssr/tokens-CbfoRC_s.mjs +0 -2
  148. package/.output/server/drizzle/0001_naive_silver_fox.sql +0 -25
  149. package/.output/server/drizzle/0002_funny_sage.sql +0 -12
  150. package/.output/server/drizzle/0003_optimal_slapstick.sql +0 -102
  151. package/.output/server/drizzle/0004_remove_grok_agent.sql +0 -5
  152. package/.output/server/drizzle/0005_heavy_gauntlet.sql +0 -8
  153. package/.output/server/drizzle/0006_tranquil_sharon_carter.sql +0 -1
  154. package/.output/server/drizzle/0007_faulty_starbolt.sql +0 -30
  155. package/.output/server/drizzle/0008_durable_lifecycle_evidence.sql +0 -15
  156. package/.output/server/drizzle/0009_ordinary_electro.sql +0 -1
  157. package/.output/server/drizzle/0010_workable_rage.sql +0 -14
  158. package/.output/server/drizzle/0011_steep_marvel_zombies.sql +0 -3
  159. package/.output/server/drizzle/0012_blocked_run_status.sql +0 -12
  160. package/.output/server/drizzle/0013_drop_run_outcome.sql +0 -2
  161. package/.output/server/drizzle/0014_luxuriant_scrambler.sql +0 -5
  162. package/.output/server/drizzle/0015_run_history_foundation.sql +0 -43
  163. package/.output/server/drizzle/0016_plural_machine_runs.sql +0 -4
  164. package/.output/server/drizzle/meta/0000_snapshot.json +0 -1333
  165. package/.output/server/drizzle/meta/0001_snapshot.json +0 -1498
  166. package/.output/server/drizzle/meta/0002_snapshot.json +0 -1576
  167. package/.output/server/drizzle/meta/0003_snapshot.json +0 -1666
  168. package/.output/server/drizzle/meta/0005_snapshot.json +0 -1666
  169. package/.output/server/drizzle/meta/0006_snapshot.json +0 -1672
  170. package/.output/server/drizzle/meta/0007_snapshot.json +0 -1787
  171. package/.output/server/drizzle/meta/0009_snapshot.json +0 -1793
  172. package/.output/server/drizzle/meta/0010_snapshot.json +0 -1891
  173. package/.output/server/drizzle/meta/0011_snapshot.json +0 -1872
  174. package/drizzle/0001_naive_silver_fox.sql +0 -25
  175. package/drizzle/0002_funny_sage.sql +0 -12
  176. package/drizzle/0003_optimal_slapstick.sql +0 -102
  177. package/drizzle/0004_remove_grok_agent.sql +0 -5
  178. package/drizzle/0005_heavy_gauntlet.sql +0 -8
  179. package/drizzle/0006_tranquil_sharon_carter.sql +0 -1
  180. package/drizzle/0007_faulty_starbolt.sql +0 -30
  181. package/drizzle/0008_durable_lifecycle_evidence.sql +0 -15
  182. package/drizzle/0009_ordinary_electro.sql +0 -1
  183. package/drizzle/0010_workable_rage.sql +0 -14
  184. package/drizzle/0011_steep_marvel_zombies.sql +0 -3
  185. package/drizzle/0012_blocked_run_status.sql +0 -12
  186. package/drizzle/0013_drop_run_outcome.sql +0 -2
  187. package/drizzle/0014_luxuriant_scrambler.sql +0 -5
  188. package/drizzle/0015_run_history_foundation.sql +0 -43
  189. package/drizzle/0016_plural_machine_runs.sql +0 -4
  190. package/drizzle/meta/0000_snapshot.json +0 -1333
  191. package/drizzle/meta/0001_snapshot.json +0 -1498
  192. package/drizzle/meta/0002_snapshot.json +0 -1576
  193. package/drizzle/meta/0003_snapshot.json +0 -1666
  194. package/drizzle/meta/0005_snapshot.json +0 -1666
  195. package/drizzle/meta/0006_snapshot.json +0 -1672
  196. package/drizzle/meta/0007_snapshot.json +0 -1787
  197. package/drizzle/meta/0009_snapshot.json +0 -1793
  198. package/drizzle/meta/0010_snapshot.json +0 -1891
  199. package/drizzle/meta/0011_snapshot.json +0 -1872
@@ -0,0 +1,914 @@
1
+ import { o as __toESM } from "../_runtime.mjs";
2
+ import { g as require_react, h as require_jsx_runtime, s as TooltipProvider } from "../_libs/@base-ui/react+[...].mjs";
3
+ import { O as useHydrated, T as selectCls, a as btn, b as lastRunOf, f as dotLabel, l as btnSm, o as btnDanger, r as Pill, s as btnPrimary, t as ErrorBanner, u as cronText, v as inputCls, x as rel, y as labelCls } from "./ui-D5KWstlb.mjs";
4
+ import { d as useNavigate } from "../_libs/@tanstack/react-router+[...].mjs";
5
+ import { _ as loadOlderRuns, d as getConfig, g as listMyTeams, h as listLoops, n as GitHubIcon, r as PievoLogo, t as GITHUB_URL, v as mintClaim } from "./SocialLinks-Cm4H8pUJ.mjs";
6
+ import { t as setActiveTeamCookie } from "./teamCookie-B7hLOIie.mjs";
7
+ import { a as getTeamDetail, d as revokeTeamInvite, f as setTeamMemberRole, i as deleteTeam, l as removeTeamMember, n as createTeam, o as leaveTeam, r as createTeamInvite, s as listManagedTeams, t as addTeamMember, u as renameTeam } from "./teamFns-j2JKjorZ.mjs";
8
+ import { r as deriveLoopLifecycle } from "./lifecycleUi-Dad3hvhm.mjs";
9
+ import { a as Timeline, c as mergeRuns, i as ModalSection, n as Modal, o as daemonConnectCommand, r as ModalHead, s as listMachines, t as MachinesModal } from "./MachinesModal-BjkcVjjx.mjs";
10
+ //#region node_modules/.nitro/vite/services/ssr/assets/DashboardView-C2LdV6J0.js
11
+ var import_react = /* @__PURE__ */ __toESM(require_react());
12
+ var import_jsx_runtime = require_jsx_runtime();
13
+ function LoopCard({ loop, onOpen, onPickRun }) {
14
+ const en = loop.enabled;
15
+ const last = lastRunOf(loop);
16
+ const lifecycle = deriveLoopLifecycle(loop);
17
+ const hydrated = useHydrated();
18
+ const [older, setOlder] = (0, import_react.useState)([]);
19
+ const runs = (0, import_react.useMemo)(() => older.length ? mergeRuns(loop.runs ?? [], older) : loop.runs ?? [], [loop.runs, older]);
20
+ const newestRun = runs.at(-1);
21
+ const latestIncidentRun = newestRun?.reportIncident ? newestRun : void 0;
22
+ const loadMore = async () => {
23
+ const oldest = runs[0];
24
+ if (!oldest) return 0;
25
+ const more = await loadOlderRuns({ data: {
26
+ loopId: loop.id,
27
+ beforeTs: oldest.ts,
28
+ limit: 16
29
+ } });
30
+ if (more.length) setOlder((prev) => mergeRuns(prev, more));
31
+ return more.length;
32
+ };
33
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
34
+ onClick: () => onOpen(loop.id),
35
+ className: `mb-[18px] cursor-pointer rounded-card border border-hairline bg-surface px-[26px] pb-5 pt-[22px] shadow-card transition-colors hover:border-wire ${en || latestIncidentRun?.reportIncident || loop.pauseCause?.kind === "failure-streak" || loop.pauseCause?.kind === "blocked" ? "" : "opacity-60"}`,
36
+ style: { animation: "fadeIn .25s cubic-bezier(0.25,0.1,0.25,1) both" },
37
+ children: [
38
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
39
+ className: "mb-6 flex flex-wrap items-center gap-3",
40
+ children: [
41
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
42
+ type: "button",
43
+ onClick: (e) => {
44
+ e.stopPropagation();
45
+ onOpen(loop.id);
46
+ },
47
+ className: "cursor-pointer rounded-sm text-left text-[17px] font-semibold tracking-[-0.01em] text-display outline-none focus-visible:ring-2 focus-visible:ring-interactive focus-visible:ring-offset-4 focus-visible:ring-offset-surface",
48
+ children: loop.name
49
+ }),
50
+ lifecycle === "stopping" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
51
+ tone: "running",
52
+ dot: "pulse",
53
+ children: "Stopping"
54
+ }) : lifecycle === "paused-finishing" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, { children: loop.pauseCause?.kind === "blocked" ? "Paused — blocked · current run finishing" : loop.pauseCause?.kind === "owner" ? "Paused by owner · current run finishing" : "Paused · current run finishing" }) : lifecycle === "deleting" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, { children: "Deleting" }) : loop.running ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
55
+ tone: "running",
56
+ dot: "pulse",
57
+ children: "Running"
58
+ }) : loop.queued && loop.reconciliationBlocking ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
59
+ tone: "outline",
60
+ children: "Queued · waiting for machine recovery"
61
+ }) : loop.queued ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
62
+ tone: "outline",
63
+ children: "Queued"
64
+ }) : null,
65
+ lifecycle === "paused" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
66
+ tone: loop.pauseCause?.kind === "blocked" ? "accent" : void 0,
67
+ children: loop.pauseCause?.kind === "blocked" ? "Paused — blocked" : loop.pauseCause?.kind === "failure-streak" ? "Paused automatically" : loop.pauseCause?.kind === "owner" ? "Paused by owner" : "Paused"
68
+ }),
69
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
70
+ className: "ml-auto min-w-0 text-right text-meta text-secondary",
71
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
72
+ className: "whitespace-nowrap",
73
+ children: [
74
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
75
+ className: "text-primary",
76
+ title: loop.schedule.mode === "cron" ? loop.schedule.cron : void 0,
77
+ children: loop.schedule.mode === "continuous" ? `continuous · ${loop.schedule.delayMinutes}m` : `${cronText(loop.schedule.cron)} · ${loop.schedule.timezone} · ${loop.schedule.overlap}`
78
+ }),
79
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
80
+ className: "mx-2.5 text-wire",
81
+ children: "·"
82
+ }),
83
+ loop.agent
84
+ ]
85
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
86
+ className: "mt-1 truncate text-caption text-disabled",
87
+ title: loop.workdir,
88
+ children: [
89
+ loop.workdir,
90
+ " · Model: ",
91
+ loop.model || "default",
92
+ " · Reasoning: ",
93
+ loop.reasoningEffort || "default"
94
+ ]
95
+ })]
96
+ })
97
+ ]
98
+ }),
99
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Timeline, {
100
+ loop,
101
+ runs,
102
+ total: loop.runCount,
103
+ onLoadMore: loadMore,
104
+ onPickRun: (run) => onPickRun(loop.id, run)
105
+ }),
106
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
107
+ className: "mt-[18px] flex items-center gap-2 text-label text-secondary",
108
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [loop.runCount, " runs"] }), last && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
109
+ "· last ",
110
+ dotLabel(last),
111
+ hydrated && ` · ${rel(last.ts)}`
112
+ ] })]
113
+ }),
114
+ latestIncidentRun?.reportIncident && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
115
+ className: "mt-1.5 text-label text-secondary",
116
+ children: latestIncidentRun.phase === "error" ? "Last run failed · Terminal report rejected" : "Last run telemetry warning · Terminal report rejected"
117
+ })
118
+ ]
119
+ });
120
+ }
121
+ /**
122
+ * Header team entry. Always visible when team data exists: a single-team user
123
+ * sees a quiet pill naming the active team (so the workspace context is never
124
+ * invisible); anyone who can reach more than one team gets the select.
125
+ *
126
+ * Switching NAVIGATES to `/t/<id>` (the explicit team URL — bookmarkable, and
127
+ * each tab keeps its own team). The cookie is still written, now only as the
128
+ * last-used default that the bare `/` redirect falls back to (no longer an
129
+ * authorization key). The navigation (and the route's `key={teamId}` remount) is
130
+ * what re-scopes the dashboard, so no refresh callback is needed.
131
+ */
132
+ function TeamSwitcher({ data }) {
133
+ const navigate = useNavigate();
134
+ if (!data || data.teams.length === 0) return null;
135
+ const selectTeam = (id) => {
136
+ setActiveTeamCookie(id);
137
+ navigate({
138
+ to: "/t/$teamId",
139
+ params: { teamId: id }
140
+ });
141
+ };
142
+ if (data.teams.length === 1) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
143
+ title: "Active team",
144
+ className: "inline-flex items-center rounded-full bg-raised px-3 py-1 text-label font-medium text-secondary",
145
+ children: data.teams[0].name
146
+ });
147
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", {
148
+ "aria-label": "Active team",
149
+ value: data.activeTeamId,
150
+ onChange: (e) => selectTeam(e.target.value),
151
+ className: "lp-select cursor-pointer rounded-full bg-raised py-1 pl-3 text-label font-medium text-secondary outline-none transition-colors hover:text-display",
152
+ children: data.teams.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
153
+ value: t.id,
154
+ children: t.name
155
+ }, t.id))
156
+ });
157
+ }
158
+ function inviteUrl(token) {
159
+ return `${typeof window !== "undefined" ? window.location.origin : ""}/invite/${token}`;
160
+ }
161
+ function CopyLink({ text }) {
162
+ const [done, setDone] = (0, import_react.useState)(false);
163
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
164
+ className: btnSm,
165
+ onClick: () => {
166
+ navigator.clipboard?.writeText(text);
167
+ setDone(true);
168
+ setTimeout(() => setDone(false), 1400);
169
+ },
170
+ children: done ? "Copied ✓" : "Copy link"
171
+ });
172
+ }
173
+ /**
174
+ * Team settings — the owner-facing surface for the full lifecycle: create,
175
+ * rename, members (add-by-email + role + remove), invite links, leave, and
176
+ * delete (with the blocked-while-loops explanation). A master list of the
177
+ * caller's teams on the left; the selected team's detail below. Every action
178
+ * calls an EXPLICIT-teamId server fn (never the active cookie), so managing team
179
+ * B while the dashboard is on team A works. Owner-only controls are hidden for a
180
+ * plain member (the server re-authorizes regardless).
181
+ */
182
+ function TeamsModal({ open, onClose, activeTeamId }) {
183
+ const navigate = useNavigate();
184
+ const [teams, setTeams] = (0, import_react.useState)([]);
185
+ const [selected, setSelected] = (0, import_react.useState)(null);
186
+ const [detail, setDetail] = (0, import_react.useState)(null);
187
+ const [err, setErr] = (0, import_react.useState)(null);
188
+ const [busy, setBusy] = (0, import_react.useState)(false);
189
+ const [newName, setNewName] = (0, import_react.useState)("");
190
+ const [renameVal, setRenameVal] = (0, import_react.useState)("");
191
+ const [email, setEmail] = (0, import_react.useState)("");
192
+ const [addRole, setAddRole] = (0, import_react.useState)("member");
193
+ const [inviteRole, setInviteRole] = (0, import_react.useState)("member");
194
+ const [freshInvite, setFreshInvite] = (0, import_react.useState)(null);
195
+ const loadTeams = (0, import_react.useCallback)(async () => {
196
+ try {
197
+ setTeams(await listManagedTeams());
198
+ } catch {}
199
+ }, []);
200
+ const loadDetail = (0, import_react.useCallback)(async (teamId) => {
201
+ const d = await getTeamDetail({ data: teamId });
202
+ if ("error" in d) {
203
+ setErr(d.error);
204
+ setDetail(null);
205
+ return;
206
+ }
207
+ setDetail(d);
208
+ setRenameVal(d.name);
209
+ }, []);
210
+ (0, import_react.useEffect)(() => {
211
+ if (!open) {
212
+ setErr(null);
213
+ setSelected(null);
214
+ setDetail(null);
215
+ setFreshInvite(null);
216
+ return;
217
+ }
218
+ loadTeams();
219
+ }, [open, loadTeams]);
220
+ (0, import_react.useEffect)(() => {
221
+ if (!open || !selected) return;
222
+ setFreshInvite(null);
223
+ loadDetail(selected);
224
+ }, [
225
+ open,
226
+ selected,
227
+ loadDetail
228
+ ]);
229
+ async function withBusy(fn) {
230
+ setBusy(true);
231
+ setErr(null);
232
+ try {
233
+ return await fn();
234
+ } finally {
235
+ setBusy(false);
236
+ }
237
+ }
238
+ async function doCreate() {
239
+ const name = newName.trim();
240
+ if (!name) return;
241
+ await withBusy(async () => {
242
+ const r = await createTeam({ data: name });
243
+ if (!r.ok) return setErr(r.error);
244
+ setNewName("");
245
+ await loadTeams();
246
+ setSelected(r.id);
247
+ });
248
+ }
249
+ async function doRename() {
250
+ if (!detail) return;
251
+ const name = renameVal.trim();
252
+ if (!name || name === detail.name) return;
253
+ await withBusy(async () => {
254
+ const r = await renameTeam({ data: {
255
+ teamId: detail.id,
256
+ name
257
+ } });
258
+ if (!r.ok) return setErr(r.error);
259
+ await loadTeams();
260
+ await loadDetail(detail.id);
261
+ });
262
+ }
263
+ async function doDelete() {
264
+ if (!detail) return;
265
+ await withBusy(async () => {
266
+ const r = await deleteTeam({ data: detail.id });
267
+ if (!r.ok) return setErr(r.error);
268
+ const removed = detail.id;
269
+ setSelected(null);
270
+ setDetail(null);
271
+ await loadTeams();
272
+ if (removed === activeTeamId) {
273
+ onClose();
274
+ navigate({ to: "/" });
275
+ }
276
+ });
277
+ }
278
+ async function doLeave() {
279
+ if (!detail) return;
280
+ await withBusy(async () => {
281
+ const r = await leaveTeam({ data: detail.id });
282
+ if (!r.ok) return setErr(r.error);
283
+ const left = detail.id;
284
+ setSelected(null);
285
+ setDetail(null);
286
+ await loadTeams();
287
+ if (left === activeTeamId) {
288
+ onClose();
289
+ navigate({ to: "/" });
290
+ }
291
+ });
292
+ }
293
+ async function doAdd() {
294
+ if (!detail) return;
295
+ const addr = email.trim();
296
+ if (!addr) return;
297
+ await withBusy(async () => {
298
+ const r = await addTeamMember({ data: {
299
+ teamId: detail.id,
300
+ email: addr,
301
+ role: addRole
302
+ } });
303
+ if (!r.ok) return setErr(r.error);
304
+ setEmail("");
305
+ await loadDetail(detail.id);
306
+ await loadTeams();
307
+ });
308
+ }
309
+ async function doSetRole(userId, role) {
310
+ if (!detail) return;
311
+ await withBusy(async () => {
312
+ const r = await setTeamMemberRole({ data: {
313
+ teamId: detail.id,
314
+ userId,
315
+ role
316
+ } });
317
+ if (!r.ok) return setErr(r.error);
318
+ await loadDetail(detail.id);
319
+ });
320
+ }
321
+ async function doRemove(userId) {
322
+ if (!detail) return;
323
+ await withBusy(async () => {
324
+ const r = await removeTeamMember({ data: {
325
+ teamId: detail.id,
326
+ userId
327
+ } });
328
+ if (!r.ok) return setErr(r.error);
329
+ await loadDetail(detail.id);
330
+ await loadTeams();
331
+ });
332
+ }
333
+ async function doInvite() {
334
+ if (!detail) return;
335
+ await withBusy(async () => {
336
+ const r = await createTeamInvite({ data: {
337
+ teamId: detail.id,
338
+ role: inviteRole
339
+ } });
340
+ if (!r.ok) return setErr(r.error);
341
+ setFreshInvite(r.token);
342
+ await loadDetail(detail.id);
343
+ });
344
+ }
345
+ async function doRevoke(token) {
346
+ if (!detail) return;
347
+ await withBusy(async () => {
348
+ const r = await revokeTeamInvite({ data: {
349
+ teamId: detail.id,
350
+ token
351
+ } });
352
+ if (!r.ok) return setErr(r.error);
353
+ if (freshInvite === token) setFreshInvite(null);
354
+ await loadDetail(detail.id);
355
+ });
356
+ }
357
+ const isOwner = detail?.role === "owner";
358
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Modal, {
359
+ open,
360
+ onClose,
361
+ wide: true,
362
+ children: [
363
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalHead, {
364
+ title: "Teams",
365
+ sub: "Create teams, manage members and roles, and invite people. Team management is owner-only; any member can create loops."
366
+ }),
367
+ err && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorBanner, {
368
+ message: err,
369
+ onDismiss: () => setErr(null),
370
+ className: "mb-2 mt-3"
371
+ }),
372
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
373
+ className: "mt-3 grid gap-6 md:grid-cols-[minmax(0,18rem)_minmax(0,1fr)]",
374
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
375
+ className: "min-w-0",
376
+ children: [
377
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalSection, { children: "My teams" }),
378
+ teams.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
379
+ className: "py-2 text-body text-secondary",
380
+ children: "No teams yet."
381
+ }),
382
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
383
+ className: "flex flex-col gap-1.5",
384
+ children: teams.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
385
+ onClick: () => setSelected(t.id),
386
+ className: `flex w-full items-center justify-between gap-2 rounded-control border px-3 py-2 text-left transition-colors ${selected === t.id ? "border-display bg-raised" : "border-hairline bg-surface hover:bg-raised"}`,
387
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
388
+ className: "min-w-0 truncate text-[15px] font-medium text-display",
389
+ children: t.name
390
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
391
+ className: "flex shrink-0 items-center gap-1.5",
392
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
393
+ tone: t.role === "owner" ? "ink" : "neutral",
394
+ children: t.role
395
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
396
+ className: "text-label text-secondary",
397
+ children: [
398
+ t.memberCount,
399
+ " member",
400
+ t.memberCount === 1 ? "" : "s"
401
+ ]
402
+ })]
403
+ })]
404
+ }) }, t.id))
405
+ }),
406
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
407
+ className: "mt-4 rounded-control border border-hairline bg-raised px-3 py-3",
408
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
409
+ className: `${labelCls} mt-0`,
410
+ children: "New team"
411
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
412
+ className: "flex gap-2",
413
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
414
+ className: inputCls,
415
+ value: newName,
416
+ onChange: (e) => setNewName(e.target.value),
417
+ placeholder: "e.g. Growth Squad",
418
+ onKeyDown: (e) => e.key === "Enter" && void doCreate()
419
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
420
+ className: btnPrimary,
421
+ disabled: busy || !newName.trim(),
422
+ onClick: () => void doCreate(),
423
+ children: "Create"
424
+ })]
425
+ })]
426
+ })
427
+ ]
428
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
429
+ className: "min-w-0",
430
+ children: !detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
431
+ className: "flex h-full items-center justify-center py-10 text-body text-secondary",
432
+ children: "Select a team to manage it, or create one."
433
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
434
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ModalSection, {
435
+ action: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
436
+ className: btnSm,
437
+ onClick: () => {
438
+ setActiveTeamCookie(detail.id);
439
+ onClose();
440
+ navigate({
441
+ to: "/t/$teamId",
442
+ params: { teamId: detail.id }
443
+ });
444
+ },
445
+ children: "Open dashboard →"
446
+ }),
447
+ children: [detail.name, detail.personal ? " · personal" : ""]
448
+ }),
449
+ isOwner && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
450
+ className: "mb-4",
451
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
452
+ className: "mb-1.5 mt-3 block text-label font-medium text-secondary",
453
+ children: "Team name"
454
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
455
+ className: "flex gap-2",
456
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
457
+ "aria-label": "Team name",
458
+ className: inputCls,
459
+ value: renameVal,
460
+ onChange: (e) => setRenameVal(e.target.value)
461
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
462
+ className: btn,
463
+ disabled: busy || !renameVal.trim() || renameVal.trim() === detail.name,
464
+ onClick: () => void doRename(),
465
+ children: "Rename"
466
+ })]
467
+ })]
468
+ }),
469
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalSection, { children: "Members" }),
470
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
471
+ className: "flex flex-col gap-2",
472
+ children: detail.members.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", {
473
+ className: "flex items-center justify-between gap-3 rounded-control border border-hairline bg-surface px-3 py-2.5",
474
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
475
+ className: "flex min-w-0 flex-col",
476
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
477
+ className: "truncate text-[15px] font-medium text-display",
478
+ children: [m.email ?? m.displayName ?? m.userId, m.isSelf && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
479
+ className: "ml-1.5 text-label text-secondary",
480
+ children: "(you)"
481
+ })]
482
+ })
483
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
484
+ className: "flex shrink-0 items-center gap-2",
485
+ children: isOwner && !m.isSelf ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
486
+ "aria-label": "Role",
487
+ className: `${selectCls} w-auto py-1.5`,
488
+ value: m.role,
489
+ onChange: (e) => void doSetRole(m.userId, e.target.value),
490
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
491
+ value: "owner",
492
+ children: "owner"
493
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
494
+ value: "member",
495
+ children: "member"
496
+ })]
497
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
498
+ className: btnDanger,
499
+ disabled: busy,
500
+ onClick: () => void doRemove(m.userId),
501
+ children: "Remove"
502
+ })] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
503
+ tone: m.role === "owner" ? "ink" : "neutral",
504
+ children: m.role
505
+ })
506
+ })]
507
+ }, m.userId))
508
+ }),
509
+ isOwner && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
510
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
511
+ className: "mt-4 rounded-control border border-hairline bg-raised px-3 py-3",
512
+ children: [
513
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
514
+ className: `mb-1.5 mt-3 block text-label font-medium text-secondary mt-0`,
515
+ children: "Add a member by email"
516
+ }),
517
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
518
+ className: "flex flex-wrap gap-2",
519
+ children: [
520
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
521
+ className: `${inputCls} min-w-[12rem] flex-1`,
522
+ value: email,
523
+ onChange: (e) => setEmail(e.target.value),
524
+ placeholder: "teammate@example.com",
525
+ onKeyDown: (e) => e.key === "Enter" && void doAdd()
526
+ }),
527
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
528
+ "aria-label": "Role for new member",
529
+ className: `${selectCls} w-auto`,
530
+ value: addRole,
531
+ onChange: (e) => setAddRole(e.target.value),
532
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
533
+ value: "member",
534
+ children: "member"
535
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
536
+ value: "owner",
537
+ children: "owner"
538
+ })]
539
+ }),
540
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
541
+ className: btnPrimary,
542
+ disabled: busy || !email.trim(),
543
+ onClick: () => void doAdd(),
544
+ children: "Add"
545
+ })
546
+ ]
547
+ }),
548
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
549
+ className: "mt-1.5 text-label text-secondary",
550
+ children: "Adds an existing Pievo account immediately. No account yet? Generate an invite link below."
551
+ })
552
+ ]
553
+ }),
554
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalSection, { children: "Invite links" }),
555
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
556
+ className: "flex flex-wrap items-center gap-2",
557
+ children: [
558
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", {
559
+ "aria-label": "Invite role",
560
+ className: `${selectCls} w-auto`,
561
+ value: inviteRole,
562
+ onChange: (e) => setInviteRole(e.target.value),
563
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
564
+ value: "member",
565
+ children: "as member"
566
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", {
567
+ value: "owner",
568
+ children: "as owner"
569
+ })]
570
+ }),
571
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
572
+ className: btn,
573
+ disabled: busy,
574
+ onClick: () => void doInvite(),
575
+ children: "Generate invite link"
576
+ }),
577
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
578
+ className: "text-label text-secondary",
579
+ children: "Single-use · expires in 7 days"
580
+ })
581
+ ]
582
+ }),
583
+ detail.invites.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
584
+ className: "mt-3 flex flex-col gap-2",
585
+ children: detail.invites.map((i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", {
586
+ className: "flex items-center justify-between gap-3 rounded-control border border-hairline bg-surface px-3 py-2.5",
587
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
588
+ className: "flex min-w-0 items-center gap-2",
589
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
590
+ tone: "neutral",
591
+ children: i.role
592
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
593
+ className: `min-w-0 truncate font-mono text-label ${freshInvite === i.token ? "text-display" : "text-secondary"}`,
594
+ children: inviteUrl(i.token)
595
+ })]
596
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
597
+ className: "flex shrink-0 items-center gap-2",
598
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CopyLink, { text: inviteUrl(i.token) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
599
+ className: btnDanger,
600
+ disabled: busy,
601
+ onClick: () => void doRevoke(i.token),
602
+ children: "Revoke"
603
+ })]
604
+ })]
605
+ }, i.token))
606
+ })
607
+ ] }),
608
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
609
+ className: "mt-6 flex items-center justify-between gap-3 border-t border-hairline pt-4",
610
+ children: [!detail.personal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
611
+ className: btn,
612
+ disabled: busy,
613
+ onClick: () => void doLeave(),
614
+ children: "Leave team"
615
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
616
+ className: "text-label text-secondary",
617
+ children: "Your personal team can't be left or deleted."
618
+ }), isOwner && !detail.personal && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
619
+ className: btnDanger,
620
+ disabled: busy || detail.loopCount > 0,
621
+ title: detail.loopCount > 0 ? `Move or delete this team's ${detail.loopCount} loop${detail.loopCount === 1 ? "" : "s"} first` : void 0,
622
+ onClick: () => void doDelete(),
623
+ children: ["Delete team", detail.loopCount > 0 ? ` (${detail.loopCount} loop${detail.loopCount === 1 ? "" : "s"})` : ""]
624
+ })]
625
+ })
626
+ ] })
627
+ })]
628
+ })
629
+ ]
630
+ });
631
+ }
632
+ function ComposeModal({ open, onClose, teamId }) {
633
+ const [token, setToken] = (0, import_react.useState)(null);
634
+ const [config, setConfig] = (0, import_react.useState)(null);
635
+ const [copied, setCopied] = (0, import_react.useState)(false);
636
+ const [error, setError] = (0, import_react.useState)(null);
637
+ const [configError, setConfigError] = (0, import_react.useState)(false);
638
+ const origin = typeof window !== "undefined" ? window.location.origin : "";
639
+ const command = token && config ? daemonConnectCommand(origin, token, config.pievoCli, config.customCli) : "";
640
+ (0, import_react.useEffect)(() => {
641
+ if (!open) return;
642
+ setToken(null);
643
+ setConfig(null);
644
+ setCopied(false);
645
+ setError(null);
646
+ setConfigError(false);
647
+ }, [open]);
648
+ (0, import_react.useEffect)(() => {
649
+ if (!open) return;
650
+ let active = true;
651
+ getConfig().then((value) => {
652
+ if (active) setConfig(value);
653
+ }).catch(() => {
654
+ if (active) setConfigError(true);
655
+ });
656
+ mintClaim({ data: teamId }).then((result) => {
657
+ if (!active) return;
658
+ if ("token" in result) setToken(result.token);
659
+ else setError(result.error);
660
+ }).catch(() => {
661
+ if (active) setError("could not mint a connect key");
662
+ });
663
+ return () => {
664
+ active = false;
665
+ };
666
+ }, [open, teamId]);
667
+ async function copyCommand() {
668
+ try {
669
+ await navigator.clipboard.writeText(command);
670
+ setCopied(true);
671
+ setTimeout(() => setCopied(false), 1500);
672
+ } catch {
673
+ setError("could not copy - select the command and copy manually");
674
+ }
675
+ }
676
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Modal, {
677
+ open,
678
+ onClose,
679
+ children: [
680
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalHead, { title: "New loop" }),
681
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
682
+ className: "mt-6",
683
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Step, {
684
+ number: "1",
685
+ continued: true,
686
+ title: "Install and connect the daemon",
687
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
688
+ className: "mt-1.5 text-body leading-snug text-secondary",
689
+ children: "Paste this command into the foreground coding-agent session where the loop should run."
690
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
691
+ className: "mt-3 flex min-w-0 items-start gap-2",
692
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
693
+ className: "min-w-0 flex-1 overflow-x-auto whitespace-pre-wrap break-all rounded-control bg-display p-4 font-mono text-label leading-relaxed text-paper",
694
+ children: command || (configError ? "Could not load the connection command. Close and try again." : token ? "Loading CLI configuration…" : "Minting a connect key…")
695
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
696
+ className: btnSm,
697
+ onClick: () => void copyCommand(),
698
+ disabled: !command,
699
+ children: copied ? "Copied ✓" : "Copy"
700
+ })]
701
+ })]
702
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Step, {
703
+ number: "2",
704
+ title: "Create the loop",
705
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
706
+ className: "mt-1.5 text-body leading-snug text-secondary",
707
+ children: "Then, in the same session, say:"
708
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
709
+ className: "mt-3 rounded-control border border-hairline bg-raised p-4 text-body font-medium text-display",
710
+ children: "Create a Pievo loop."
711
+ })]
712
+ })]
713
+ }),
714
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
715
+ className: "mt-4 text-body text-accent",
716
+ children: ["Error: ", error]
717
+ })
718
+ ]
719
+ });
720
+ }
721
+ function Step({ number, title, continued = false, children }) {
722
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
723
+ className: "flex gap-3.5",
724
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
725
+ className: "flex flex-col items-center",
726
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
727
+ className: "flex size-5 shrink-0 items-center justify-center rounded-full border border-display text-micro font-medium leading-none text-display",
728
+ children: number
729
+ }), continued && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "mt-1 w-px flex-1 bg-hairline" })]
730
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
731
+ className: continued ? "min-w-0 flex-1 pb-7" : "min-w-0 flex-1",
732
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", {
733
+ className: "text-label font-semibold text-display",
734
+ children: title
735
+ }), children]
736
+ })]
737
+ });
738
+ }
739
+ /** Loops, machines, and teams change between polls.
740
+ *
741
+ * `teamId` (the `/t/<id>` route's team, in id form or undefined in open mode)
742
+ * scopes every list fn EXPLICITLY - so a tab on /t/A and one on /t/B show
743
+ * different teams simultaneously, independent of the shared last-used cookie. */
744
+ async function fetchLiveData(teamId) {
745
+ const [loops, machines, teams] = await Promise.all([
746
+ listLoops({ data: teamId }),
747
+ listMachines({ data: teamId }),
748
+ listMyTeams({ data: teamId })
749
+ ]);
750
+ return {
751
+ loops,
752
+ machines,
753
+ teams
754
+ };
755
+ }
756
+ /**
757
+ * The dashboard body, shared by the `/` (open mode) and `/t/<teamId>` routes. It
758
+ * renders from its own fetch-then-set poll state, seeded once from the route
759
+ * loader's data, and scopes every fetch to `teamId` so the view is pinned to the
760
+ * URL's team (multi-tab safe). The route mounts it with `key={teamId}` so a
761
+ * team switch (a `/t/<id>` navigation) re-seeds state from the new loader data.
762
+ */
763
+ function DashboardView({ teamId, initial }) {
764
+ const [live, setLive] = (0, import_react.useState)(null);
765
+ const { loops, machines, teams } = live ?? initial;
766
+ const online = machines.filter((m) => m.online).length;
767
+ const navigate = useNavigate();
768
+ const [composeOpen, setComposeOpen] = (0, import_react.useState)(false);
769
+ const [machinesOpen, setMachinesOpen] = (0, import_react.useState)(false);
770
+ const [teamsOpen, setTeamsOpen] = (0, import_react.useState)(false);
771
+ const refetch = (0, import_react.useCallback)(async () => {
772
+ try {
773
+ setLive(await fetchLiveData(teamId));
774
+ } catch {}
775
+ }, [teamId]);
776
+ const openRef = (0, import_react.useRef)(false);
777
+ openRef.current = composeOpen || machinesOpen || teamsOpen;
778
+ const anyRunning = loops.some((j) => j.running);
779
+ (0, import_react.useEffect)(() => {
780
+ const t = setInterval(() => {
781
+ if (!openRef.current) refetch();
782
+ }, anyRunning ? 3e3 : 1e4);
783
+ return () => clearInterval(t);
784
+ }, [refetch, anyRunning]);
785
+ const activeOn = loops.filter((j) => j.enabled).length;
786
+ const cardProps = () => ({
787
+ onOpen: (id) => void navigate({
788
+ to: "/loops/$loopId",
789
+ params: { loopId: id }
790
+ }),
791
+ onPickRun: (loopId, run) => void navigate({
792
+ to: "/loops/$loopId/runs/$runId",
793
+ params: {
794
+ loopId,
795
+ runId: run.id
796
+ }
797
+ })
798
+ });
799
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TooltipProvider, {
800
+ delay: 120,
801
+ children: [
802
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("header", {
803
+ className: "glass glass-bar sticky top-0 z-50",
804
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
805
+ className: "mx-auto flex max-w-[1180px] items-center gap-3 px-8 py-2.5",
806
+ children: [
807
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PievoLogo, { size: 30 }),
808
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
809
+ className: "text-[18px] font-semibold tracking-[-0.015em] text-display",
810
+ children: "Pievo"
811
+ }),
812
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TeamSwitcher, { data: teams }),
813
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1" }),
814
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
815
+ href: GITHUB_URL,
816
+ target: "_blank",
817
+ rel: "noreferrer",
818
+ "aria-label": "GitHub repository",
819
+ title: "GitHub",
820
+ className: headerIconBtn,
821
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitHubIcon, { className: "size-[17px]" })
822
+ }),
823
+ teams && teams.teams.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
824
+ onClick: () => setTeamsOpen(true),
825
+ className: headerBtn,
826
+ children: "Teams"
827
+ }),
828
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
829
+ onClick: () => setMachinesOpen(true),
830
+ className: `${headerBtn} gap-1.5`,
831
+ children: [
832
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `inline-block size-1.5 rounded-full ${online ? "bg-rubik-green" : "bg-disabled"}` }),
833
+ online,
834
+ " ",
835
+ online === 1 ? "machine" : "machines",
836
+ " online"
837
+ ]
838
+ }),
839
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
840
+ onClick: () => setComposeOpen(true),
841
+ className: "inline-flex shrink-0 cursor-pointer items-center rounded-full bg-display px-3.5 py-1.5 text-meta font-medium text-paper transition-opacity hover:opacity-85",
842
+ children: "New Loop"
843
+ })
844
+ ]
845
+ })
846
+ }),
847
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", {
848
+ className: "mx-auto max-w-[1180px] px-8 pb-24",
849
+ children: [
850
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
851
+ className: "pb-2 pt-14 text-center",
852
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
853
+ className: "font-pixel text-[clamp(28px,4.5vw,38px)] leading-[1.15] text-display",
854
+ children: "What should happen while you sleep?"
855
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
856
+ className: "mt-7",
857
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
858
+ onClick: () => setComposeOpen(true),
859
+ className: "inline-flex cursor-pointer items-center gap-1.5 rounded-full bg-display px-6 py-2.5 text-body font-medium text-paper transition-opacity hover:opacity-85",
860
+ children: ["Start a new loop ", /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
861
+ "aria-hidden": true,
862
+ children: "→"
863
+ })]
864
+ })
865
+ })]
866
+ }),
867
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
868
+ className: "mb-5 mt-12 flex items-baseline gap-2.5",
869
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
870
+ className: "text-body font-semibold text-display",
871
+ children: "Active loops"
872
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
873
+ className: "text-label text-secondary",
874
+ children: loops.length ? `${activeOn} scheduled · ${loops.length} total` : ""
875
+ })]
876
+ }),
877
+ loops.length ? loops.map((j) => /* @__PURE__ */ (0, import_react.createElement)(LoopCard, {
878
+ loop: j,
879
+ ...cardProps(),
880
+ key: j.id
881
+ })) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
882
+ className: "py-16 text-center",
883
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
884
+ className: "text-[15px] text-secondary",
885
+ children: "No loops yet"
886
+ }), !loops.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
887
+ className: "mt-1.5 text-body text-disabled",
888
+ children: "Create a scheduled prompt for a connected machine."
889
+ })]
890
+ })
891
+ ]
892
+ }),
893
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComposeModal, {
894
+ open: composeOpen,
895
+ teamId,
896
+ onClose: () => setComposeOpen(false)
897
+ }),
898
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MachinesModal, {
899
+ open: machinesOpen,
900
+ onClose: () => setMachinesOpen(false),
901
+ teamId
902
+ }),
903
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TeamsModal, {
904
+ open: teamsOpen,
905
+ onClose: () => setTeamsOpen(false),
906
+ activeTeamId: teamId
907
+ })
908
+ ]
909
+ });
910
+ }
911
+ var headerBtn = "inline-flex shrink-0 cursor-pointer items-center rounded-full px-3 py-1.5 text-meta font-medium text-secondary transition-colors hover:bg-raised hover:text-display";
912
+ var headerIconBtn = "inline-flex shrink-0 cursor-pointer items-center rounded-full p-1.5 text-secondary transition-colors hover:bg-raised hover:text-display";
913
+ //#endregion
914
+ export { fetchLiveData as n, DashboardView as t };