@kky42/pievo-server 2.4.14 → 4.1.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.
Files changed (95) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/MarkdownView-BuFKr9Jg.js +66 -0
  3. package/.output/public/assets/{actionUi-5PtES4q0.js → actionUi-BZrLpgHT.js} +1 -1
  4. package/.output/public/assets/app-x1CeeY7N.css +2 -0
  5. package/.output/public/assets/index-D0FIKiiW.js +10 -0
  6. package/.output/public/assets/loops._loopId-DTkIz4C0.js +2 -0
  7. package/.output/public/assets/loops._loopId_.runs._runId-CsV2Ih4Z.js +3 -0
  8. package/.output/public/assets/preload-helper-BSxGu4d0.js +3 -0
  9. package/.output/public/assets/{routes-Dm_3J2aV.js → routes-BIOJZsig.js} +1 -1
  10. package/.output/public/assets/routes-m4Z90DTR.js +1 -0
  11. package/.output/server/_chunks/ssr-renderer.mjs +1 -1
  12. package/.output/server/_libs/{drizzle-orm+postgres.mjs → drizzle-orm.mjs} +93 -2218
  13. package/.output/server/_ssr/{MachinesModal-BaEtzycr.mjs → MachinesModal-CLBZxZkq.mjs} +47 -47
  14. package/.output/server/_ssr/MarkdownView-D1J7ImYF.mjs +70 -0
  15. package/.output/server/_ssr/{actionUi-5HOCZnom.mjs → actionUi-DE-Pb2tv.mjs} +1 -2
  16. package/.output/server/_ssr/artifactFiles-B0NPRDOR.mjs +52 -0
  17. package/.output/server/_ssr/{artifactKind-DwDr8IYc.mjs → artifactKind-a2oxT2wQ.mjs} +4 -10
  18. package/.output/server/_ssr/{boot-C472kLwY.mjs → boot-DmpKtb0A.mjs} +2659 -890
  19. package/.output/server/_ssr/lifecycleUi-DEjM8Etm.mjs +106 -0
  20. package/.output/server/_ssr/{loopApi-DT751TJA.mjs → loopApi-DK1lQPrx.mjs} +53 -69
  21. package/.output/server/_ssr/{loopProjection-qaD2I48a.mjs → loopProjection-DdFivf7-.mjs} +6 -24
  22. package/.output/server/_ssr/{loops._loopId-CXgw2Rz1.mjs → loops._loopId-DVCvGSEc.mjs} +36 -116
  23. package/.output/server/_ssr/{loops._loopId_.runs._runId-SASSqOfy.mjs → loops._loopId_.runs._runId-BXbTfLlC.mjs} +35 -39
  24. package/.output/server/_ssr/{loops._loopId_.runs._runId-Dk94Z0Jq.mjs → loops._loopId_.runs._runId-Dr6E-FA5.mjs} +3 -8
  25. package/.output/server/_ssr/{machineFns-BSrIgn3f.mjs → machineFns-DLRHgDdS.mjs} +13 -50
  26. package/.output/server/_ssr/networkAccess-CIlK3ION.mjs +30 -0
  27. package/.output/server/_ssr/router-BhHsTGqu.mjs +355 -0
  28. package/.output/server/_ssr/routes-CCallyCT.mjs +12 -0
  29. package/.output/server/_ssr/{routes-Cr-9yDEH.mjs → routes-CL2Kef-Q.mjs} +91 -50
  30. package/.output/server/_ssr/{routes-DRBS4Zi4.mjs → routes-ngpX7h8D.mjs} +2 -4
  31. package/.output/server/_ssr/ssr.mjs +25 -34
  32. package/.output/server/_ssr/start-B5_9Z5W-.mjs +35 -0
  33. package/.output/server/{_tanstack-start-manifest_v-DxLIQ0cc.mjs → _tanstack-start-manifest_v-BPiJ4yWT.mjs} +9 -15
  34. package/.output/server/drizzle/0000_baseline.sql +6 -97
  35. package/.output/server/drizzle/0001_loop_files.sql +9 -0
  36. package/.output/server/drizzle/meta/_journal.json +3 -17
  37. package/.output/server/index.mjs +50 -50
  38. package/drizzle/0000_baseline.sql +6 -97
  39. package/drizzle/0001_loop_files.sql +9 -0
  40. package/drizzle/meta/_journal.json +3 -17
  41. package/package.json +4 -8
  42. package/scripts/pievo-server.mjs +49 -47
  43. package/scripts/server-cli-lib.mjs +21 -12
  44. package/scripts/server-network.mjs +139 -0
  45. package/.output/public/assets/SignIn-CIFwnZtF.js +0 -1
  46. package/.output/public/assets/app-DvHMhs7S.css +0 -2
  47. package/.output/public/assets/index-CRX2pu0W.js +0 -10
  48. package/.output/public/assets/loops._loopId-BsuV-Rmf.js +0 -67
  49. package/.output/public/assets/loops._loopId_.runs._runId-DpK_fzmZ.js +0 -3
  50. package/.output/public/assets/preload-helper-sAZOZb3G.js +0 -5
  51. package/.output/public/assets/routes-DxF_s3cD.js +0 -1
  52. package/.output/server/_libs/@aws-crypto/crc32+[...].mjs +0 -419
  53. package/.output/server/_libs/@aws-sdk/checksums+[...].mjs +0 -10732
  54. package/.output/server/_libs/@aws-sdk/client-s3+[...].mjs +0 -9475
  55. package/.output/server/_libs/@aws-sdk/credential-provider-http+[...].mjs +0 -137
  56. package/.output/server/_libs/@aws-sdk/credential-provider-ini+[...].mjs +0 -440
  57. package/.output/server/_libs/@aws-sdk/credential-provider-process+[...].mjs +0 -62
  58. package/.output/server/_libs/@aws-sdk/credential-provider-sso+[...].mjs +0 -818
  59. package/.output/server/_libs/@aws-sdk/credential-provider-web-identity+[...].mjs +0 -59
  60. package/.output/server/_libs/@smithy/credential-provider-imds+[...].mjs +0 -342
  61. package/.output/server/_libs/aws-crypto__crc32c.mjs +0 -314
  62. package/.output/server/_libs/aws-sdk__nested-clients.mjs +0 -2671
  63. package/.output/server/_ssr/SignIn-sHQaBQJC.mjs +0 -53
  64. package/.output/server/_ssr/artifactFiles-DO43wwXA.mjs +0 -60
  65. package/.output/server/_ssr/artifacts-Cd7Qx3Qp.mjs +0 -2
  66. package/.output/server/_ssr/auth-0AamrwHo.mjs +0 -38355
  67. package/.output/server/_ssr/auth-client-lSMb_Idt.mjs +0 -1070
  68. package/.output/server/_ssr/bun-sqlite-dialect-BW9W1_Ps-DLIXocA0.mjs +0 -162
  69. package/.output/server/_ssr/core-RrmWoKne.mjs +0 -5
  70. package/.output/server/_ssr/d1-sqlite-dialect-BLC8LXE6-BoFMIPr_.mjs +0 -112
  71. package/.output/server/_ssr/db-BzvMQ2kx.mjs +0 -2
  72. package/.output/server/_ssr/dist-ClzqkwxD.mjs +0 -342
  73. package/.output/server/_ssr/factory-ixUjZqJn.mjs +0 -6903
  74. package/.output/server/_ssr/kysely-adapter-DkZbmKgV.mjs +0 -2
  75. package/.output/server/_ssr/kysely-migration-tables-JkVUjPF_-bPAX9LR_.mjs +0 -19
  76. package/.output/server/_ssr/logger-DDjsYB2X.mjs +0 -2
  77. package/.output/server/_ssr/loopTags-CXx5UdGf.mjs +0 -3356
  78. package/.output/server/_ssr/node-sqlite-dialect-BbUqnnYv.mjs +0 -162
  79. package/.output/server/_ssr/router-BlLlhfJe.mjs +0 -543
  80. package/.output/server/_ssr/routes-DTBltabU.mjs +0 -27
  81. package/.output/server/_ssr/start-4ifArtKx.mjs +0 -4
  82. package/.output/server/_ssr/store-vDZ9INSa.mjs +0 -2
  83. package/.output/server/_ssr/tokens-BHiCElYD.mjs +0 -2
  84. package/.output/server/drizzle/0001_add_pi_agent.sql +0 -9
  85. package/.output/server/drizzle/0002_remove_teams.sql +0 -15
  86. package/.output/server/drizzle/0003_add_loop_tags.sql +0 -5
  87. package/.output/server/node_modules/tslib/modules/index.js +0 -70
  88. package/.output/server/node_modules/tslib/modules/package.json +0 -3
  89. package/.output/server/node_modules/tslib/package.json +0 -47
  90. package/.output/server/node_modules/tslib/tslib.js +0 -484
  91. package/.output/server/package.json +0 -9
  92. package/drizzle/0001_add_pi_agent.sql +0 -9
  93. package/drizzle/0002_remove_teams.sql +0 -15
  94. package/drizzle/0003_add_loop_tags.sql +0 -5
  95. package/scripts/prestart.mjs +0 -66
@@ -1,19 +1,17 @@
1
1
  import { o as __toESM } from "../_runtime.mjs";
2
- import { Nn as validateLoopTags, s as CODING_AGENTS } from "./loopTags-CXx5UdGf.mjs";
2
+ import { J as validateLoopTags, h as CODING_AGENTS } from "./boot-DmpKtb0A.mjs";
3
3
  import { g as require_react, h as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
4
4
  import { d as useNavigate, u as Link } from "../_libs/@tanstack/react-router+[...].mjs";
5
- import { E as stopLoop, O as useSession, S as runLoop, T as startLoop, b as patchLoop, c as deleteLoop, d as getArtifacts, i as OpenModeWarning, l as deriveLoopLifecycle, m as getLoopDetail, s as daemonStopSupport, t as DAEMON_UPGRADE_REQUIRED, u as getArtifact, v as loadOlderRuns, x as pauseLoop } from "./auth-client-lSMb_Idt.mjs";
6
- import { A as tsShort, E as runPulseStyle, M as useFlash, O as sectionHeadCls, S as labelCls, _ as fmt, a as Loading, b as humanBytes, c as btn, d as btnQuiet, g as dur, i as LoadErrorCard, k as selectCls, l as btnDanger, m as dotLabel, n as ErrorBanner, p as dotColor, r as FlashLine, t as ConfirmBar, u as btnPrimary, w as rel, x as inputCls, y as focusRing } from "./actionUi-5HOCZnom.mjs";
7
- import { a as Timeline, c as mergeRuns, n as MachinesModal, t as LoopOverview } from "./MachinesModal-BaEtzycr.mjs";
8
- import { n as artifactKind, t as Route } from "./artifactKind-DwDr8IYc.mjs";
9
- import { t as SignIn } from "./SignIn-sHQaBQJC.mjs";
10
- import { t as purify } from "../_libs/dompurify.mjs";
11
- import { t as g } from "../_libs/marked.mjs";
12
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId-CXgw2Rz1.js
5
+ import { _ as runLoop, a as deriveLoopLifecycle, g as pauseLoop, h as patchLoop, i as deleteLoop, l as getLoopDetail, o as getArtifact, p as loadOlderRuns, r as daemonStopSupport, s as getArtifacts, t as DAEMON_UPGRADE_REQUIRED, v as startLoop, y as stopLoop } from "./lifecycleUi-DEjM8Etm.mjs";
6
+ import { A as tsShort, E as runPulseStyle, M as useFlash, O as sectionHeadCls, S as labelCls, _ as fmt, a as Loading, b as humanBytes, c as btn, d as btnQuiet, g as dur, i as LoadErrorCard, k as selectCls, l as btnDanger, m as dotLabel, n as ErrorBanner, p as dotColor, r as FlashLine, t as ConfirmBar, u as btnPrimary, w as rel, x as inputCls, y as focusRing } from "./actionUi-DE-Pb2tv.mjs";
7
+ import { a as Timeline, l as mergeRuns, n as MachinesModal, t as LoopOverview } from "./MachinesModal-CLBZxZkq.mjs";
8
+ import { n as artifactKind, t as Route } from "./artifactKind-a2oxT2wQ.mjs";
9
+ import { t as MarkdownView } from "./MarkdownView-D1J7ImYF.mjs";
10
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId-DVCvGSEc.js
13
11
  var import_react = /* @__PURE__ */ __toESM(require_react());
14
12
  var import_jsx_runtime = require_jsx_runtime();
15
- /** Download URL for one artifact. Each path segment is encoded independently so
16
- * nested artifact paths continue to match the catch-all route. */
13
+ /** Download URL for one loop file. Each path segment is encoded independently so
14
+ * the name continues to match the catch-all route. */
17
15
  function downloadHref(loopId, path) {
18
16
  const encodedPath = path.split("/").map(encodeURIComponent).join("/");
19
17
  return `/api/artifact/${encodeURIComponent(loopId)}/${encodedPath}`;
@@ -22,67 +20,6 @@ function inlineHref(loopId, path) {
22
20
  return `${downloadHref(loopId, path)}?view=inline`;
23
21
  }
24
22
  /**
25
- * Shared markdown → sanitized HTML pipeline: marked (GFM) → DOMPurify (an
26
- * allowlisted prose subset, NO script/handlers). ONE sanitizer config for every
27
- * artifact markdown surface so the allowlist cannot drift between viewers.
28
- * Render the output under
29
- * the `.artifact-markdown` styles.
30
- */
31
- var MD_SANITIZE = {
32
- ALLOWED_TAGS: [
33
- "h1",
34
- "h2",
35
- "h3",
36
- "h4",
37
- "h5",
38
- "h6",
39
- "p",
40
- "br",
41
- "hr",
42
- "strong",
43
- "b",
44
- "em",
45
- "i",
46
- "del",
47
- "s",
48
- "code",
49
- "pre",
50
- "blockquote",
51
- "ul",
52
- "ol",
53
- "li",
54
- "a",
55
- "span",
56
- "table",
57
- "thead",
58
- "tbody",
59
- "tr",
60
- "th",
61
- "td",
62
- "img"
63
- ],
64
- ALLOWED_ATTR: [
65
- "href",
66
- "title",
67
- "target",
68
- "rel",
69
- "src",
70
- "alt"
71
- ]
72
- };
73
- function renderMarkdown(content) {
74
- return purify.sanitize(g.parse(content, {
75
- async: false,
76
- gfm: true
77
- }), MD_SANITIZE);
78
- }
79
- function MarkdownView({ content }) {
80
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
81
- className: "artifact-markdown px-5 py-4",
82
- dangerouslySetInnerHTML: { __html: (0, import_react.useMemo)(() => renderMarkdown(content), [content]) }
83
- });
84
- }
85
- /**
86
23
  * Shared pieces of the artifact content viewer. The Files panel uses this one
87
24
  * dispatch so binary/oversize copy and type-appropriate rendering cannot drift.
88
25
  *
@@ -97,8 +34,8 @@ function MarkdownView({ content }) {
97
34
  * rides as an image, which does not execute embedded scripts).
98
35
  * - markdown → the shared markdown pipeline (Preview) with a raw Source toggle.
99
36
  * - text → monospace source.
100
- * Everything keeps a Download fallback; an oversize (metadata-only) artifact has
101
- * no synced bytes to render, so it says so rather than showing an empty pane.
37
+ * Everything keeps a Download fallback; a legacy oversize/binary entry has no
38
+ * renderable bytes, so it says so rather than showing an empty pane.
102
39
  */
103
40
  function ViewerHead({ path, meta, action }) {
104
41
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -277,7 +214,6 @@ function ArtifactBody({ loopId, file }) {
277
214
  kind
278
215
  }, file.path);
279
216
  }
280
- var basename = (path) => path.split("/").pop() || path;
281
217
  function LoopFilesPanel({ loopId, configuredPaths, running }) {
282
218
  const [artifacts, setArtifacts] = (0, import_react.useState)(null);
283
219
  const [selected, setSelected] = (0, import_react.useState)(null);
@@ -310,14 +246,19 @@ function LoopFilesPanel({ loopId, configuredPaths, running }) {
310
246
  className: "mb-2.5 flex items-end justify-between gap-3 border-b border-hairline pb-1.5",
311
247
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h2", {
312
248
  className: "text-label font-semibold text-secondary",
313
- children: ["Artifacts", artifacts ? ` (${files.length})` : ""]
249
+ children: ["Files", artifacts ? ` (${files.length})` : ""]
314
250
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
315
251
  className: "text-caption font-medium text-disabled",
316
- children: ["Configured exact paths: ", configuredPaths.length]
252
+ children: [
253
+ "MEMORY.md + ",
254
+ configuredPaths.length,
255
+ " artifact",
256
+ configuredPaths.length === 1 ? "" : "s"
257
+ ]
317
258
  })]
318
259
  }), files.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
319
260
  className: "rounded-card border border-hairline bg-surface px-5 py-10 text-center text-body text-disabled shadow-card",
320
- children: artifacts == null ? "Loading…" : configuredPaths.length ? "No configured artifact has been collected yet." : "No artifact paths configured."
261
+ children: artifacts == null ? "Loading…" : "No loop file has been written yet. Runs create MEMORY.md and any configured artifacts."
321
262
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
322
263
  className: "grid h-[min(600px,68vh)] grid-cols-1 overflow-hidden rounded-card border border-hairline bg-surface shadow-card sm:grid-cols-[210px_1fr]",
323
264
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("nav", {
@@ -330,17 +271,10 @@ function LoopFilesPanel({ loopId, configuredPaths, running }) {
330
271
  type: "button",
331
272
  onClick: () => setSelected(file.path),
332
273
  className: `flex w-full items-center border-l-2 px-3 py-2 text-left transition-colors ${activeRow ? "border-display bg-raised" : "border-transparent hover:bg-raised/60"}`,
333
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
334
- className: "min-w-0 flex-1",
335
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
336
- className: `block truncate font-mono text-label ${activeRow ? "text-display" : "text-primary"}`,
337
- title: file.path,
338
- children: basename(file.path)
339
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
340
- className: "block truncate font-mono text-micro text-disabled",
341
- title: file.path,
342
- children: file.path
343
- })]
274
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
275
+ className: `block min-w-0 flex-1 truncate font-mono text-label ${activeRow ? "text-display" : "text-primary"}`,
276
+ title: file.path,
277
+ children: file.path
344
278
  })
345
279
  }) }, file.path);
346
280
  })
@@ -349,7 +283,7 @@ function LoopFilesPanel({ loopId, configuredPaths, running }) {
349
283
  className: "min-w-0 overflow-y-auto",
350
284
  children: active && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ViewerHead, {
351
285
  path: active.path,
352
- meta: [active.size != null ? humanBytes(active.size) : "", `collected ${fmt(active.updatedAt)}`].filter(Boolean).join(" · ")
286
+ meta: [active.size != null ? humanBytes(active.size) : "", `updated ${fmt(active.updatedAt)}`].filter(Boolean).join(" · ")
353
287
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArtifactBody, {
354
288
  loopId,
355
289
  file: active
@@ -529,7 +463,7 @@ var LoopForm = (0, import_react.forwardRef)(function LoopForm({ initial }, ref)
529
463
  noChange: form.noChange,
530
464
  block: form.block
531
465
  },
532
- artifacts: form.artifacts.split(/\r?\n/).filter((path) => path.length > 0)
466
+ artifacts: form.artifacts.split(/\r?\n/).filter((name) => name.length > 0)
533
467
  };
534
468
  } }));
535
469
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -757,10 +691,10 @@ var LoopForm = (0, import_react.forwardRef)(function LoopForm({ initial }, ref)
757
691
  }),
758
692
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Section, {
759
693
  title: "Artifacts",
760
- hint: "Optional exact paths relative to the working directory. One path per line; globs are not supported."
694
+ hint: "Optional .md or .html file names (up to 4, 2 MB each) that runs update and the dashboard renders. One name per line. Every loop also has MEMORY.md."
761
695
  }),
762
696
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextArea, {
763
- label: "Artifact paths",
697
+ label: "Artifact files",
764
698
  value: form.artifacts,
765
699
  onChange: (value) => set("artifacts", value),
766
700
  rows: 6,
@@ -1034,7 +968,7 @@ function LoopDetailView({ id }) {
1034
968
  className: "mt-3",
1035
969
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfirmBar, {
1036
970
  prompt: "Stop this loop and delete its server data?",
1037
- note: "This stops the loop and deletes server history and collected artifacts. Files in the working directory are not deleted. If the machine is unreachable, its local process may continue running.",
971
+ note: "This stops the loop and deletes server history, the loop memory, and artifacts. Files in the working directory are not deleted. If the machine is unreachable, its local process may continue running.",
1038
972
  cta: "Delete",
1039
973
  danger: true,
1040
974
  busy,
@@ -1145,13 +1079,13 @@ function LoopDetailView({ id }) {
1145
1079
  })
1146
1080
  }),
1147
1081
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Config, {
1148
- label: "Artifact paths",
1082
+ label: "Artifacts",
1149
1083
  children: loop.artifacts.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
1150
1084
  className: "space-y-1",
1151
- children: loop.artifacts.map((path) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", {
1085
+ children: loop.artifacts.map((name) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", {
1152
1086
  className: "break-all font-mono",
1153
- children: path
1154
- }) }, path))
1087
+ children: name
1088
+ }) }, name))
1155
1089
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1156
1090
  className: "text-disabled",
1157
1091
  children: "None"
@@ -1206,7 +1140,7 @@ function EditHead({ name, onBack }) {
1206
1140
  }),
1207
1141
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1208
1142
  className: "mt-1.5 text-meta text-secondary",
1209
- children: "Edit tags, schedule, execution settings, prompt, status definitions, and exact artifact paths."
1143
+ children: "Edit tags, schedule, execution settings, prompt, status definitions, and artifact files."
1210
1144
  })
1211
1145
  ] });
1212
1146
  }
@@ -1286,26 +1220,12 @@ function RunsSection({ loopId, summary, runs, older, onMore, onPickRun }) {
1286
1220
  }
1287
1221
  /**
1288
1222
  * Loop detail page — `/loops/$loopId`: a loop header and action toolbar, the
1289
- * exact artifact Files panel, and the runs timeline. The view owns its own
1290
- * data + self-poll (ssr:false so the session cookie rides along with its fetches,
1291
- * like the dashboard loader). Run rows link on to `/loops/$loopId/runs/$runId`.
1292
- *
1293
- * Auth-gated exactly like the dashboard (`/`): Better Auth's session hook keeps a
1294
- * logged-out or expired deep link from mounting the ownership-scoped data view.
1223
+ * loop Files panel, and the runs timeline. The view owns its own data + self-poll.
1224
+ * Run rows link on to `/loops/$loopId/runs/$runId`.
1295
1225
  */
1296
1226
  function LoopDetailPage() {
1297
1227
  const { loopId } = Route.useParams();
1298
- const { auth } = Route.useLoaderData() ?? { auth: { enabled: false } };
1299
- const { data: session, isPending } = useSession();
1300
- if (auth.enabled && isPending) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("main", {
1301
- className: "mx-auto max-w-[1360px] px-8 pt-10",
1302
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Loading, {})
1303
- });
1304
- if (auth.enabled && !session) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SignIn, { callbackURL: `/loops/${loopId}` });
1305
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [!auth.enabled && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
1306
- className: "mx-auto max-w-[1360px] px-8 pt-6",
1307
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OpenModeWarning, {})
1308
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoopDetailView, { id: loopId })] });
1228
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoopDetailView, { id: loopId });
1309
1229
  }
1310
1230
  //#endregion
1311
1231
  export { LoopDetailPage as component };
@@ -1,11 +1,11 @@
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
3
  import { u as Link } from "../_libs/@tanstack/react-router+[...].mjs";
4
- import { D as stopRun, O as useSession, h as getRunDiff, i as OpenModeWarning, m as getLoopDetail, v as loadOlderRuns } from "./auth-client-lSMb_Idt.mjs";
5
- import { E as runPulseStyle, O as sectionHeadCls, _ as fmt, a as Loading, b as humanBytes, c as btn, g as dur, i as LoadErrorCard, l as btnDanger, o as Pill, s as StatusPill, v as fnum } from "./actionUi-5HOCZnom.mjs";
6
- import { t as SignIn } from "./SignIn-sHQaBQJC.mjs";
7
- import { t as Route } from "./loops._loopId_.runs._runId-Dk94Z0Jq.mjs";
8
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-SASSqOfy.js
4
+ import { b as stopRun, l as getLoopDetail, p as loadOlderRuns, u as getRunDiff } from "./lifecycleUi-DEjM8Etm.mjs";
5
+ import { E as runPulseStyle, O as sectionHeadCls, _ as fmt, a as Loading, b as humanBytes, c as btn, g as dur, i as LoadErrorCard, l as btnDanger, o as Pill, s as StatusPill, v as fnum } from "./actionUi-DE-Pb2tv.mjs";
6
+ import { t as MarkdownView } from "./MarkdownView-D1J7ImYF.mjs";
7
+ import { t as Route } from "./loops._loopId_.runs._runId-Dr6E-FA5.mjs";
8
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-BXbTfLlC.js
9
9
  var import_react = /* @__PURE__ */ __toESM(require_react());
10
10
  var import_jsx_runtime = require_jsx_runtime();
11
11
  /**
@@ -463,8 +463,8 @@ function RunDetailView({ loopId, runId }) {
463
463
  type: "button",
464
464
  onClick: onStop,
465
465
  className: btnDanger,
466
- disabled: run.phase === "running" && detail.machine.daemonProtocol !== 4,
467
- title: run.phase === "running" && detail.machine.daemonProtocol !== 4 ? "Daemon upgrade required to stop a running process. Run `npm install -g @kky42/pievo@latest`, then `pievo daemon restart`." : void 0,
466
+ disabled: run.phase === "running" && detail.machine.daemonProtocol !== 5,
467
+ title: run.phase === "running" && detail.machine.daemonProtocol !== 5 ? "Daemon upgrade required to stop a running process. Run `npm install -g @kky42/pievo@latest`, then `pievo daemon restart`." : void 0,
468
468
  children: "Stop run"
469
469
  })]
470
470
  })]
@@ -528,15 +528,25 @@ function RunDetailView({ loopId, runId }) {
528
528
  ]
529
529
  })
530
530
  }),
531
+ run.fileNotices && run.fileNotices.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, {
532
+ label: "Loop files not kept",
533
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
534
+ className: "list-disc space-y-1.5 pl-5 text-body text-primary",
535
+ children: run.fileNotices.map((notice) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", {
536
+ className: "break-words",
537
+ children: notice
538
+ }, notice))
539
+ })
540
+ }),
531
541
  run.message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, {
532
542
  label: "Report",
533
543
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
534
- className: "whitespace-pre-wrap break-words rounded-control border border-hairline bg-raised px-4 py-3.5 text-body leading-relaxed text-primary",
535
- children: run.message
544
+ className: "break-words rounded-control border border-hairline bg-raised text-body leading-relaxed text-primary",
545
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownView, { content: run.message })
536
546
  })
537
547
  }),
538
548
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Changes, { run }),
539
- run.finalText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, {
549
+ run.finalText && !run.message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, {
540
550
  label: "Final output",
541
551
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
542
552
  className: "whitespace-pre-wrap break-words text-body text-primary",
@@ -593,41 +603,27 @@ function RunDetailView({ loopId, runId }) {
593
603
  * `_` on the `$loopId` segment opts it out of the loop page's component so the run
594
604
  * gets its own full surface, deep-linkable + browser-back friendly) rather than a
595
605
  * modal or an inline panel. It resolves the run from the loop's detail payload
596
- * (reusing getLoopDetail) and the existing artifact diff.
597
- *
598
- * Auth-gated like the dashboard (`/`) so Better Auth's session hook keeps a
599
- * logged-out or expired deep link from mounting the run view.
606
+ * (reusing getLoopDetail) and the existing loop-file diff.
600
607
  */
601
608
  function RunDetailPage() {
602
609
  const { loopId, runId } = Route.useParams();
603
- const { auth } = Route.useLoaderData() ?? { auth: { enabled: false } };
604
- const { data: session, isPending } = useSession();
605
- if (auth.enabled && isPending) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("main", {
606
- className: "mx-auto max-w-[1360px] px-8 pt-10",
607
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Loading, {})
608
- });
609
- if (auth.enabled && !session) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SignIn, { callbackURL: `/loops/${loopId}/runs/${runId}` });
610
610
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", {
611
611
  className: "mx-auto max-w-[1360px] px-8 pb-24 pt-6",
612
- children: [
613
- !auth.enabled && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OpenModeWarning, { className: "mb-6" }),
614
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
615
- className: "mb-5",
616
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Link, {
617
- to: "/loops/$loopId",
618
- params: { loopId },
619
- className: "inline-flex items-center gap-1.5 text-label font-medium text-secondary transition-colors hover:text-display",
620
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
621
- "aria-hidden": true,
622
- children: "←"
623
- }), " Back to loop"]
624
- })
625
- }),
626
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunDetailView, {
627
- loopId,
628
- runId
612
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
613
+ className: "mb-5",
614
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Link, {
615
+ to: "/loops/$loopId",
616
+ params: { loopId },
617
+ className: "inline-flex items-center gap-1.5 text-label font-medium text-secondary transition-colors hover:text-display",
618
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
619
+ "aria-hidden": true,
620
+ children: "←"
621
+ }), " Back to loop"]
629
622
  })
630
- ]
623
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunDetailView, {
624
+ loopId,
625
+ runId
626
+ })]
631
627
  });
632
628
  }
633
629
  //#endregion
@@ -1,20 +1,15 @@
1
1
  import { c as createFileRoute, s as lazyRouteComponent } from "../_libs/@tanstack/react-router+[...].mjs";
2
- import { f as getAuthState } from "./auth-client-lSMb_Idt.mjs";
3
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-Dk94Z0Jq.js
4
- var $$splitComponentImporter = () => import("./loops._loopId_.runs._runId-SASSqOfy.mjs");
2
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-Dr6E-FA5.js
3
+ var $$splitComponentImporter = () => import("./loops._loopId_.runs._runId-BXbTfLlC.mjs");
5
4
  /**
6
5
  * Run detail PAGE — `/loops/$loopId/runs/$runId`. A standalone page (the trailing
7
6
  * `_` on the `$loopId` segment opts it out of the loop page's component so the run
8
7
  * gets its own full surface, deep-linkable + browser-back friendly) rather than a
9
8
  * modal or an inline panel. It resolves the run from the loop's detail payload
10
- * (reusing getLoopDetail) and the existing artifact diff.
11
- *
12
- * Auth-gated like the dashboard (`/`) so Better Auth's session hook keeps a
13
- * logged-out or expired deep link from mounting the run view.
9
+ * (reusing getLoopDetail) and the existing loop-file diff.
14
10
  */
15
11
  var Route = createFileRoute("/loops/$loopId_/runs/$runId")({
16
12
  ssr: false,
17
- loader: async () => ({ auth: await getAuthState() }),
18
13
  component: lazyRouteComponent($$splitComponentImporter, "component")
19
14
  });
20
15
  //#endregion
@@ -1,17 +1,7 @@
1
- import { Et as listMachinesForUser, G as forceDeleteMachine, Lt as mintDeviceToken, Nt as loopsForMachine, Pt as machineIdFromToken, Sn as sha256, Tt as listMachines$1, et as getMachine, j as createMachine$1, jn as updateMachine } from "./loopTags-CXx5UdGf.mjs";
2
- import { a as daemonNeedsUpdate, n as MIN_DAEMON_VERSION, o as ensureServer } from "./boot-C472kLwY.mjs";
3
- import { requestScope } from "./auth-0AamrwHo.mjs";
4
- import { n as createServerFn } from "./ssr.mjs";
1
+ import { H as sha256, I as mintDeviceToken, N as loopsForMachine, P as machineIdFromToken, S as getMachine, _ as createMachine$1, k as listMachines$1, o as daemonNeedsUpdate, q as updateMachine, s as ensureServer, t as MIN_DAEMON_VERSION, y as forceDeleteMachine } from "./boot-DmpKtb0A.mjs";
2
+ import { r as createServerFn } from "./ssr.mjs";
5
3
  import { t as createServerRpc } from "./createServerRpc-BD8Xn_0_.mjs";
6
- //#region node_modules/.nitro/vite/services/ssr/assets/machineFns-BSrIgn3f.js
7
- /** Open mode sees every machine; auth mode sees only the signed-in owner's rows. */
8
- function machineInScope(machine, scope) {
9
- return !scope.enforce || !!scope.userId && machine.userId === scope.userId;
10
- }
11
- /** A device token fully impersonates its machine, so auth mode keeps it owner-only. */
12
- function tokenVisibleTo(machine, scope) {
13
- return machineInScope(machine, scope);
14
- }
4
+ //#region node_modules/.nitro/vite/services/ssr/assets/machineFns-DLRHgDdS.js
15
5
  /**
16
6
  * Latest published @kky42/pievo version — a cached, fail-silent lookup of the
17
7
  * npm dist-tag `latest`, so the web can tell a user their daemon is outdated and
@@ -93,21 +83,10 @@ var LatestDaemonVersion = class {
93
83
  };
94
84
  var latestDaemonVersion = new LatestDaemonVersion();
95
85
  /**
96
- * Machine-management server functions. In auth mode, the signed-in user is the
97
- * sole human authorization boundary. Open mode intentionally shares all machine
98
- * administration, including reconnect tokens.
86
+ * Machine-management server functions. Pievo is single-user: anyone who can
87
+ * reach the dashboard administers every machine, including reconnect tokens.
99
88
  */
100
- /** Missing and out-of-scope machines both resolve to undefined. */
101
- async function scopedMachine(id) {
102
- const scope = await requestScope();
103
- if (scope.enforce && !scope.userId) return { scope };
104
- const candidate = await getMachine(id);
105
- return {
106
- scope,
107
- machine: candidate && machineInScope(candidate, scope) ? candidate : void 0
108
- };
109
- }
110
- async function toSummary(machine, scope) {
89
+ async function toSummary(machine) {
111
90
  return {
112
91
  id: machine.id,
113
92
  name: machine.name,
@@ -121,7 +100,7 @@ async function toSummary(machine, scope) {
121
100
  latestDaemonVersion: latestDaemonVersion.get(),
122
101
  needsUpdate: daemonNeedsUpdate(machine.daemonVersion),
123
102
  requiredDaemonVersion: MIN_DAEMON_VERSION,
124
- token: tokenVisibleTo(machine, scope) ? machine.token ?? null : null,
103
+ token: machine.token ?? null,
125
104
  loopCount: (await loopsForMachine(machine.id)).length
126
105
  };
127
106
  }
@@ -132,10 +111,8 @@ var listMachines_createServerFn_handler = createServerRpc({
132
111
  }, (opts) => listMachines.__executeServer(opts));
133
112
  var listMachines = createServerFn({ method: "GET" }).handler(listMachines_createServerFn_handler, async () => {
134
113
  await ensureServer();
135
- const scope = await requestScope();
136
- if (scope.enforce && !scope.userId) return [];
137
- const machines = scope.enforce ? await listMachinesForUser(scope.userId) : await listMachines$1();
138
- return Promise.all(machines.filter((machine) => machine.name.trim()).map((machine) => toSummary(machine, scope)));
114
+ const machines = await listMachines$1();
115
+ return Promise.all(machines.filter((machine) => machine.name.trim()).map(toSummary));
139
116
  });
140
117
  var createMachine_createServerFn_handler = createServerRpc({
141
118
  id: "ed679b4e1e05e903a5d4ba4a70d9b9d883991ab22ed59b4d2d69078941818415",
@@ -144,13 +121,10 @@ var createMachine_createServerFn_handler = createServerRpc({
144
121
  }, (opts) => createMachine.__executeServer(opts));
145
122
  var createMachine = createServerFn({ method: "POST" }).handler(createMachine_createServerFn_handler, async () => {
146
123
  await ensureServer();
147
- const scope = await requestScope();
148
- if (scope.enforce && !scope.userId) return { error: "not signed in" };
149
124
  const token = mintDeviceToken();
150
125
  const id = machineIdFromToken(token);
151
126
  await createMachine$1({
152
127
  id,
153
- userId: scope.userId ?? "shared",
154
128
  name: "",
155
129
  tokenHash: sha256(token),
156
130
  token,
@@ -168,8 +142,8 @@ var machineStatus_createServerFn_handler = createServerRpc({
168
142
  }, (opts) => machineStatus.__executeServer(opts));
169
143
  var machineStatus = createServerFn({ method: "GET" }).validator((id) => id).handler(machineStatus_createServerFn_handler, async ({ data: id }) => {
170
144
  await ensureServer();
171
- const { scope, machine } = await scopedMachine(id);
172
- return machine ? toSummary(machine, scope) : null;
145
+ const machine = await getMachine(id);
146
+ return machine ? toSummary(machine) : null;
173
147
  });
174
148
  var finalizeMachine_createServerFn_handler = createServerRpc({
175
149
  id: "27d7eba0b6a2257949392fdd56057275a5c5366484ae01faf3b5897a225ecd83",
@@ -180,8 +154,6 @@ var finalizeMachine = createServerFn({ method: "POST" }).validator((data) => dat
180
154
  await ensureServer();
181
155
  const name = data.name?.trim();
182
156
  if (!name) return { ok: false };
183
- const { machine } = await scopedMachine(data.id);
184
- if (!machine) return { ok: false };
185
157
  return { ok: !!await updateMachine(data.id, { name }) };
186
158
  });
187
159
  var deleteMachine_createServerFn_handler = createServerRpc({
@@ -191,17 +163,8 @@ var deleteMachine_createServerFn_handler = createServerRpc({
191
163
  }, (opts) => deleteMachine.__executeServer(opts));
192
164
  var deleteMachine = createServerFn({ method: "POST" }).validator((id) => id).handler(deleteMachine_createServerFn_handler, async ({ data: id }) => {
193
165
  const { scheduler } = await ensureServer();
194
- const { scope, machine } = await scopedMachine(id);
195
- if (scope.enforce && !scope.userId) return {
196
- ok: false,
197
- error: "unauthorized"
198
- };
199
- if (!machine) return {
200
- ok: false,
201
- error: "machine not found"
202
- };
203
- const deleted = scope.enforce ? await forceDeleteMachine(id, scope.userId) : await forceDeleteMachine(id);
204
- if (deleted.state === "forbidden" || deleted.state === "not-found") return {
166
+ const deleted = await forceDeleteMachine(id);
167
+ if (deleted.state === "not-found") return {
205
168
  ok: false,
206
169
  error: "machine not found"
207
170
  };
@@ -0,0 +1,30 @@
1
+ import { isIP } from "node:net";
2
+ //#region node_modules/.nitro/vite/services/ssr/assets/networkAccess-CIlK3ION.js
3
+ /**
4
+ * Pievo has no login, so the network is the access boundary: only this
5
+ * machine, private LAN ranges, and Tailscale peers may reach any route.
6
+ * Anyone who passes this check can run prompts on every connected machine.
7
+ * Requests proxied by `tailscale serve` arrive from loopback.
8
+ *
9
+ * IPv4: loopback 127/8, RFC 1918 (10/8, 172.16/12, 192.168/16), link-local
10
+ * 169.254/16, and CGNAT 100.64/10 (Tailscale addresses). IPv6: loopback,
11
+ * link-local fe80::/10, and unique-local fc00::/7 (includes Tailscale's
12
+ * fd7a:115c:a1e0::/48). IPv4-mapped IPv6 is checked as IPv4.
13
+ */
14
+ function isTrustedPeer(address) {
15
+ if (!address) return false;
16
+ const ip = address.toLowerCase().replace(/^::ffff:(?=\d+\.\d+\.\d+\.\d+$)/, "").split("%")[0];
17
+ const family = isIP(ip);
18
+ if (family === 4) {
19
+ const [a, b] = ip.split(".").map(Number);
20
+ return a === 127 || a === 10 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 169 && b === 254 || a === 100 && b >= 64 && b <= 127;
21
+ }
22
+ if (family === 6) {
23
+ if (ip === "::1") return true;
24
+ const first = parseInt(ip.split(":")[0] || "0", 16);
25
+ return (first & 65472) === 65152 || (first & 65024) === 64512;
26
+ }
27
+ return false;
28
+ }
29
+ //#endregion
30
+ export { isTrustedPeer };