@kky42/pievo-server 2.4.12 → 2.4.14

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 (88) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/SignIn-CIFwnZtF.js +1 -0
  3. package/.output/public/assets/{ui-s5IMg6RF.js → actionUi-5PtES4q0.js} +1 -1
  4. package/.output/public/assets/app-DvHMhs7S.css +2 -0
  5. package/.output/public/assets/index-CRX2pu0W.js +10 -0
  6. package/.output/public/assets/loops._loopId-BsuV-Rmf.js +67 -0
  7. package/.output/public/assets/loops._loopId_.runs._runId-DpK_fzmZ.js +3 -0
  8. package/.output/public/assets/{preload-helper-Bs7oCAsw.js → preload-helper-sAZOZb3G.js} +3 -3
  9. package/.output/public/assets/routes-Dm_3J2aV.js +1 -0
  10. package/.output/public/assets/routes-DxF_s3cD.js +1 -0
  11. package/.output/server/_libs/@tanstack/react-router+[...].mjs +1 -1
  12. package/.output/server/_ssr/{MachinesModal-BhRw7oOx.mjs → MachinesModal-BaEtzycr.mjs} +24 -57
  13. package/.output/server/_ssr/{SignIn-BhoZIOUp.mjs → SignIn-sHQaBQJC.mjs} +3 -3
  14. package/.output/server/_ssr/{ui-DGz-q9MT.mjs → actionUi-5HOCZnom.mjs} +115 -4
  15. package/.output/server/_ssr/{artifactFiles-ChuJ4PzC.mjs → artifactFiles-DO43wwXA.mjs} +4 -3
  16. package/.output/server/_ssr/{artifactKind-DXumOv7c.mjs → artifactKind-DwDr8IYc.mjs} +6 -14
  17. package/.output/server/_ssr/artifacts-Cd7Qx3Qp.mjs +2 -0
  18. package/.output/server/_ssr/{auth-LwPp_iim.mjs → auth-0AamrwHo.mjs} +44 -124
  19. package/.output/server/_ssr/{SocialLinks-DVCLmql0.mjs → auth-client-lSMb_Idt.mjs} +143 -109
  20. package/.output/server/_ssr/boot-C472kLwY.mjs +3947 -0
  21. package/.output/server/_ssr/{bun-sqlite-dialect-BW9W1_Ps-CHMDkKqA.mjs → bun-sqlite-dialect-BW9W1_Ps-DLIXocA0.mjs} +2 -2
  22. package/.output/server/_ssr/{d1-sqlite-dialect-BLC8LXE6-DYum6qky.mjs → d1-sqlite-dialect-BLC8LXE6-BoFMIPr_.mjs} +2 -2
  23. package/.output/server/_ssr/db-BzvMQ2kx.mjs +2 -0
  24. package/.output/server/_ssr/{dist-bWqs5sTy.mjs → dist-ClzqkwxD.mjs} +2 -2
  25. package/.output/server/_ssr/{factory-pBwrjVTI.mjs → factory-ixUjZqJn.mjs} +2 -2
  26. package/.output/server/_ssr/{kysely-adapter-DMf5pFTf.mjs → kysely-adapter-DkZbmKgV.mjs} +1 -1
  27. package/.output/server/_ssr/logger-DDjsYB2X.mjs +2 -0
  28. package/.output/server/_ssr/{loopApi-Im6JSK0p.mjs → loopApi-DT751TJA.mjs} +30 -90
  29. package/.output/server/_ssr/{loopProjection-DIpGR6rp.mjs → loopProjection-qaD2I48a.mjs} +5 -2
  30. package/.output/server/_ssr/loopTags-CXx5UdGf.mjs +3356 -0
  31. package/.output/server/_ssr/{loops._loopId-B5iPseDG.mjs → loops._loopId-CXgw2Rz1.mjs} +144 -46
  32. package/.output/server/_ssr/{loops._loopId_.runs._runId-DVtG0xYb.mjs → loops._loopId_.runs._runId-Dk94Z0Jq.mjs} +6 -13
  33. package/.output/server/_ssr/{loops._loopId_.runs._runId-BA-P6sGA.mjs → loops._loopId_.runs._runId-SASSqOfy.mjs} +31 -23
  34. package/.output/server/_ssr/{machineFns-Bb9KNXun.mjs → machineFns-BSrIgn3f.mjs} +42 -108
  35. package/.output/server/_ssr/{node-sqlite-dialect-Dho9YjXb.mjs → node-sqlite-dialect-BbUqnnYv.mjs} +2 -2
  36. package/.output/server/_ssr/{router-C8iotXtk.mjs → router-BlLlhfJe.mjs} +26 -40
  37. package/.output/server/_ssr/routes-Cr-9yDEH.mjs +491 -0
  38. package/.output/server/_ssr/{routes-FO0MFfYK.mjs → routes-DRBS4Zi4.mjs} +4 -9
  39. package/.output/server/_ssr/routes-DTBltabU.mjs +27 -0
  40. package/.output/server/_ssr/ssr.mjs +28 -88
  41. package/.output/server/_ssr/store-vDZ9INSa.mjs +2 -0
  42. package/.output/server/_ssr/tokens-BHiCElYD.mjs +2 -0
  43. package/.output/server/_tanstack-start-manifest_v-DxLIQ0cc.mjs +52 -0
  44. package/.output/server/drizzle/0002_remove_teams.sql +15 -0
  45. package/.output/server/drizzle/0003_add_loop_tags.sql +5 -0
  46. package/.output/server/drizzle/meta/_journal.json +14 -0
  47. package/.output/server/index.mjs +47 -96
  48. package/drizzle/0002_remove_teams.sql +15 -0
  49. package/drizzle/0003_add_loop_tags.sql +5 -0
  50. package/drizzle/meta/_journal.json +14 -0
  51. package/package.json +2 -2
  52. package/.output/public/assets/MachinesModal-ByHejech.js +0 -1
  53. package/.output/public/assets/SignIn-Cf4HOpdF.js +0 -1
  54. package/.output/public/assets/actionUi-yYVvoOTn.js +0 -1
  55. package/.output/public/assets/app-D5Hy4BcS.css +0 -2
  56. package/.output/public/assets/index-D504r3X9.js +0 -10
  57. package/.output/public/assets/invite._token-B0EKVyrU.js +0 -1
  58. package/.output/public/assets/invite._token-BSnGRyUw.js +0 -1
  59. package/.output/public/assets/lifecycleUi-C0DaC6NM.js +0 -1
  60. package/.output/public/assets/loops._loopId-j5EB2PLW.js +0 -67
  61. package/.output/public/assets/loops._loopId_.runs._runId-BICo_pRz.js +0 -3
  62. package/.output/public/assets/routes-4N6mrQK_.js +0 -1
  63. package/.output/public/assets/routes-DZU0Tqka.js +0 -1
  64. package/.output/public/assets/t._teamId-CAUjvMZ6.js +0 -1
  65. package/.output/public/assets/t._teamId-Cqzd69sO.js +0 -1
  66. package/.output/server/_ssr/DashboardView-DrKNkmcX.mjs +0 -867
  67. package/.output/server/_ssr/actionUi-Cf6DuEC2.mjs +0 -119
  68. package/.output/server/_ssr/artifacts-DOLHJHcd.mjs +0 -2
  69. package/.output/server/_ssr/boot-BIxtZHU-.mjs +0 -7466
  70. package/.output/server/_ssr/boot-D8cj4uZj.mjs +0 -2
  71. package/.output/server/_ssr/db-BphSJs66.mjs +0 -2
  72. package/.output/server/_ssr/invite._token-CPSvdDox.mjs +0 -27
  73. package/.output/server/_ssr/invite._token-CRBsTSTZ.mjs +0 -84
  74. package/.output/server/_ssr/invite._token-CaTLt92a.mjs +0 -20
  75. package/.output/server/_ssr/lifecycleUi-DauKavgx.mjs +0 -50
  76. package/.output/server/_ssr/logger-EinA2J6y.mjs +0 -2
  77. package/.output/server/_ssr/routes-CsV1weub.mjs +0 -26
  78. package/.output/server/_ssr/routes-fiMzFtcP.mjs +0 -39
  79. package/.output/server/_ssr/runDiff-BLdot9mi.mjs +0 -2
  80. package/.output/server/_ssr/store-CFCp_ggC.mjs +0 -2
  81. package/.output/server/_ssr/t._teamId-BYA4Dnjp.mjs +0 -43
  82. package/.output/server/_ssr/t._teamId-CKsrgN86.mjs +0 -32
  83. package/.output/server/_ssr/t._teamId-D1BTwhWf.mjs +0 -31
  84. package/.output/server/_ssr/teamCookie-B7hLOIie.mjs +0 -10
  85. package/.output/server/_ssr/teamFns-DfqtuBW2.mjs +0 -451
  86. package/.output/server/_ssr/teamFns-cI_tsiCv.mjs +0 -32
  87. package/.output/server/_ssr/tokens-CUz4LDLm.mjs +0 -2
  88. package/.output/server/_tanstack-start-manifest_v-xSxVtI1J.mjs +0 -86
@@ -1,18 +1,15 @@
1
1
  import { o as __toESM } from "../_runtime.mjs";
2
- import { s as CODING_AGENTS } from "./boot-BIxtZHU-.mjs";
2
+ import { Nn as validateLoopTags, s as CODING_AGENTS } from "./loopTags-CXx5UdGf.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 { C as startLoop, E as useSession, _ as loadOlderRuns, b as pauseLoop, c as getArtifact, l as getArtifacts, p as getLoopDetail, s as deleteLoop, w as stopLoop, x as runLoop, y as patchLoop } from "./SocialLinks-DVCLmql0.mjs";
6
- import { E as tsShort, S as runPulseStyle, T as selectCls, _ as inputCls, a as btn, b as rel, c as btnQuiet, d as dotLabel, g as humanBytes, m as fmt, n as Loading, o as btnDanger, p as dur, r as Pill, s as btnPrimary, t as ErrorBanner, u as dotColor, v as labelCls, w as sectionHeadCls } from "./ui-DGz-q9MT.mjs";
7
- import { t as setActiveTeamCookie } from "./teamCookie-B7hLOIie.mjs";
8
- import { t as SignIn } from "./SignIn-BhoZIOUp.mjs";
9
- import { i as useFlash, n as FlashLine, r as LoadErrorCard, t as ConfirmBar } from "./actionUi-Cf6DuEC2.mjs";
10
- import { n as daemonStopSupport, r as deriveLoopLifecycle, t as DAEMON_UPGRADE_REQUIRED } from "./lifecycleUi-DauKavgx.mjs";
11
- import { l as mergeRuns, n as MachinesModal, o as Timeline, t as LoopOverview } from "./MachinesModal-BhRw7oOx.mjs";
12
- import { n as artifactKind, t as Route } from "./artifactKind-DXumOv7c.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";
13
10
  import { t as purify } from "../_libs/dompurify.mjs";
14
11
  import { t as g } from "../_libs/marked.mjs";
15
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId-B5iPseDG.js
12
+ //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId-CXgw2Rz1.js
16
13
  var import_react = /* @__PURE__ */ __toESM(require_react());
17
14
  var import_jsx_runtime = require_jsx_runtime();
18
15
  /** Download URL for one artifact. Each path segment is encoded independently so
@@ -371,6 +368,8 @@ function initState(initial) {
371
368
  const schedule = initial?.schedule;
372
369
  return {
373
370
  name: initial?.name ?? "",
371
+ tags: initial?.tags ?? [],
372
+ tagDraft: "",
374
373
  mode: schedule?.mode ?? "cron",
375
374
  cron: schedule?.mode === "cron" ? schedule.cron : "0 */3 * * *",
376
375
  timezone: schedule?.mode === "cron" ? schedule.timezone : Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC",
@@ -450,10 +449,37 @@ function TextArea({ label, value, onChange, rows = 4, mono, hint }) {
450
449
  var LoopForm = (0, import_react.forwardRef)(function LoopForm({ initial }, ref) {
451
450
  const [form, setForm] = (0, import_react.useState)(() => initState(initial));
452
451
  const [error, setError] = (0, import_react.useState)(null);
452
+ const [tagError, setTagError] = (0, import_react.useState)(null);
453
+ const tagsLabelId = (0, import_react.useId)();
454
+ const tagsHintId = (0, import_react.useId)();
455
+ const tagsErrorId = (0, import_react.useId)();
456
+ const tagGroupRef = (0, import_react.useRef)(null);
457
+ const tagInputRef = (0, import_react.useRef)(null);
453
458
  const set = (key, value) => setForm((current) => ({
454
459
  ...current,
455
460
  [key]: value
456
461
  }));
462
+ const commitTagDraft = () => {
463
+ const additions = form.tagDraft.trim() ? [form.tagDraft] : [];
464
+ if (!additions.length) {
465
+ set("tagDraft", "");
466
+ return;
467
+ }
468
+ const result = validateLoopTags([...form.tags, ...additions]);
469
+ if (!result.ok) {
470
+ setTagError(result.detail);
471
+ return;
472
+ }
473
+ setForm((current) => ({
474
+ ...current,
475
+ tags: result.value,
476
+ tagDraft: ""
477
+ }));
478
+ setTagError(null);
479
+ if (result.value.length === 4) requestAnimationFrame(() => {
480
+ if (document.activeElement === document.body) tagGroupRef.current?.focus();
481
+ });
482
+ };
457
483
  (0, import_react.useImperativeHandle)(ref, () => ({ read() {
458
484
  const missing = [
459
485
  ["Name", form.name],
@@ -467,6 +493,12 @@ var LoopForm = (0, import_react.forwardRef)(function LoopForm({ initial }, ref)
467
493
  setError(`${missing[0]} is required.`);
468
494
  return null;
469
495
  }
496
+ const tagResult = validateLoopTags([...form.tags, ...form.tagDraft.trim() ? [form.tagDraft] : []]);
497
+ if (!tagResult.ok) {
498
+ setTagError(tagResult.detail);
499
+ setError(null);
500
+ return null;
501
+ }
470
502
  const delayMinutes = Number(form.delayMinutes);
471
503
  if (form.mode === "continuous" && (!Number.isInteger(delayMinutes) || delayMinutes < 1)) {
472
504
  setError("Continuous delay must be an integer of at least 1 minute.");
@@ -482,8 +514,10 @@ var LoopForm = (0, import_react.forwardRef)(function LoopForm({ initial }, ref)
482
514
  delayMinutes
483
515
  };
484
516
  setError(null);
517
+ setTagError(null);
485
518
  return {
486
519
  name: form.name.trim(),
520
+ tags: tagResult.value,
487
521
  schedule,
488
522
  workdir: form.workdir.trim(),
489
523
  agent: form.agent,
@@ -509,6 +543,79 @@ var LoopForm = (0, import_react.forwardRef)(function LoopForm({ initial }, ref)
509
543
  value: form.name,
510
544
  onChange: (value) => set("name", value)
511
545
  }),
546
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
547
+ className: "min-w-0",
548
+ children: [
549
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
550
+ id: tagsLabelId,
551
+ className: labelCls,
552
+ children: "Tags"
553
+ }),
554
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
555
+ ref: tagGroupRef,
556
+ role: "group",
557
+ tabIndex: -1,
558
+ "aria-labelledby": tagsLabelId,
559
+ "aria-describedby": `${tagsHintId}${tagError ? ` ${tagsErrorId}` : ""}`,
560
+ "aria-invalid": tagError ? "true" : void 0,
561
+ className: "flex min-h-10 flex-wrap items-center gap-1.5 rounded-control border border-wire bg-surface px-2.5 py-1.5 outline-none transition-shadow focus-within:border-transparent focus-within:shadow-focus",
562
+ children: [form.tags.map((tag) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
563
+ className: "inline-flex h-6 max-w-full items-center gap-1 rounded-full bg-raised px-2.5 text-caption font-medium text-secondary",
564
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
565
+ className: "max-w-52 truncate",
566
+ title: tag,
567
+ children: tag
568
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
569
+ type: "button",
570
+ "aria-label": `Remove tag ${tag}`,
571
+ onClick: () => {
572
+ set("tags", form.tags.filter((value) => value !== tag));
573
+ setTagError(null);
574
+ requestAnimationFrame(() => (tagInputRef.current ?? tagGroupRef.current)?.focus());
575
+ },
576
+ className: `-mr-1 inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-full text-disabled transition-colors hover:bg-surface hover:text-primary ${focusRing}`,
577
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
578
+ "aria-hidden": true,
579
+ children: "×"
580
+ })
581
+ })]
582
+ }, tag)), form.tags.length < 4 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
583
+ ref: tagInputRef,
584
+ "aria-label": "Add tag",
585
+ value: form.tagDraft,
586
+ placeholder: form.tags.length ? "Add another…" : "Add a tag…",
587
+ onChange: (event) => {
588
+ set("tagDraft", event.target.value);
589
+ setTagError(null);
590
+ },
591
+ onKeyDown: (event) => {
592
+ if (event.nativeEvent.isComposing) return;
593
+ if (event.key === "Enter") {
594
+ event.preventDefault();
595
+ commitTagDraft();
596
+ } else if (event.key === "Backspace" && !form.tagDraft && form.tags.length) set("tags", form.tags.slice(0, -1));
597
+ },
598
+ onBlur: commitTagDraft,
599
+ className: "h-6 min-w-28 flex-1 bg-transparent px-1 text-sm text-primary outline-none placeholder:text-disabled"
600
+ })]
601
+ }),
602
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
603
+ id: tagsHintId,
604
+ className: hintCls,
605
+ children: [
606
+ "Up to ",
607
+ 4,
608
+ ". Press Enter to add."
609
+ ]
610
+ }),
611
+ tagError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
612
+ id: tagsErrorId,
613
+ role: "alert",
614
+ className: "mt-1 text-caption text-accent",
615
+ children: tagError
616
+ })
617
+ ]
618
+ }),
512
619
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
513
620
  className: "min-w-0",
514
621
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
@@ -790,7 +897,6 @@ function LoopDetailView({ id }) {
790
897
  const canStop = !deleting && !!summary.running && stopSupport.supported;
791
898
  const online = detail.machine.online;
792
899
  const asleep = detail.machine.presence === "asleep";
793
- const crossTeam = detail.team && !detail.team.isActive ? detail.team : null;
794
900
  if (editing) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Shell, {
795
901
  back,
796
902
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditHead, {
@@ -854,24 +960,10 @@ function LoopDetailView({ id }) {
854
960
  loopId: id,
855
961
  runId: run.id
856
962
  }
857
- }),
858
- extraPill: crossTeam ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
859
- tone: "outline",
860
- children: crossTeam.name
861
- }) : void 0
963
+ })
862
964
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
863
965
  className: "mt-5 border-t border-hairline pt-4",
864
966
  children: [
865
- crossTeam && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TeamBanner, {
866
- team: crossTeam,
867
- onSwitch: () => {
868
- setActiveTeamCookie(crossTeam.id);
869
- navigate({
870
- to: "/t/$teamId",
871
- params: { teamId: crossTeam.id }
872
- });
873
- }
874
- }),
875
967
  detail.machine.needsUpdate && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MachineBanner, {
876
968
  tone: "warn",
877
969
  text: `Daemon update required · server requires v${detail.machine.requiredDaemonVersion}; machine reports ${detail.machine.daemonVersion ? `v${detail.machine.daemonVersion}` : "an unknown version"}.`,
@@ -989,6 +1081,20 @@ function LoopDetailView({ id }) {
989
1081
  children: configurationScheduleText
990
1082
  })
991
1083
  }),
1084
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Config, {
1085
+ label: "Tags",
1086
+ children: loop.tags.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
1087
+ className: "flex flex-wrap gap-1.5",
1088
+ children: loop.tags.map((tag) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1089
+ className: "inline-flex h-[22px] max-w-52 items-center truncate rounded-full bg-raised px-2.5 text-caption font-medium text-secondary",
1090
+ title: tag,
1091
+ children: tag
1092
+ }, tag))
1093
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
1094
+ className: "text-disabled",
1095
+ children: "None"
1096
+ })
1097
+ }),
992
1098
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Config, {
993
1099
  label: "Working directory",
994
1100
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", {
@@ -1070,20 +1176,6 @@ function Config({ label, children }) {
1070
1176
  children
1071
1177
  })] });
1072
1178
  }
1073
- function TeamBanner({ team, onSwitch }) {
1074
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1075
- className: "mb-2.5 flex flex-wrap items-center gap-3 rounded-control border border-hairline bg-raised px-4 py-2.5 text-meta text-secondary",
1076
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1077
- "Viewing a loop in ",
1078
- team.name,
1079
- " - not your active team."
1080
- ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
1081
- onClick: onSwitch,
1082
- className: "ml-auto font-medium text-interactive underline",
1083
- children: "Switch to this team"
1084
- })]
1085
- });
1086
- }
1087
1179
  function MachineBanner({ text, action, onAction, tone }) {
1088
1180
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
1089
1181
  className: `mb-2.5 flex flex-wrap items-center gap-3 rounded-control border px-4 py-2.5 text-meta ${tone === "warn" ? "border-warn/40 bg-warn/10" : "border-hairline bg-raised"}`,
@@ -1114,7 +1206,7 @@ function EditHead({ name, onBack }) {
1114
1206
  }),
1115
1207
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
1116
1208
  className: "mt-1.5 text-meta text-secondary",
1117
- children: "Edit the stored schedule, execution settings, prompt, status definitions, and exact artifact paths."
1209
+ children: "Edit tags, schedule, execution settings, prompt, status definitions, and exact artifact paths."
1118
1210
  })
1119
1211
  ] });
1120
1212
  }
@@ -1198,16 +1290,22 @@ function RunsSection({ loopId, summary, runs, older, onMore, onPickRun }) {
1198
1290
  * data + self-poll (ssr:false so the session cookie rides along with its fetches,
1199
1291
  * like the dashboard loader). Run rows link on to `/loops/$loopId/runs/$runId`.
1200
1292
  *
1201
- * Auth-gated exactly like the dashboard (`/`): the loader checks getAuthState +
1202
- * the session so a logged-out/expired DEEP LINK shows the sign-in CTA instead of
1203
- * a raw `loop not found` error from the ownership-blocked data fetch.
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.
1204
1295
  */
1205
1296
  function LoopDetailPage() {
1206
1297
  const { loopId } = Route.useParams();
1207
1298
  const { auth } = Route.useLoaderData() ?? { auth: { enabled: false } };
1208
1299
  const { data: session, isPending } = useSession();
1209
- if (auth?.enabled && !isPending && !session) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SignIn, { callbackURL: `/loops/${loopId}` });
1210
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoopDetailView, { id: loopId });
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 })] });
1211
1309
  }
1212
1310
  //#endregion
1213
1311
  export { LoopDetailPage as component };
@@ -1,7 +1,7 @@
1
1
  import { c as createFileRoute, s as lazyRouteComponent } from "../_libs/@tanstack/react-router+[...].mjs";
2
- import { i as authClient, u as getAuthState } from "./SocialLinks-DVCLmql0.mjs";
3
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-DVtG0xYb.js
4
- var $$splitComponentImporter = () => import("./loops._loopId_.runs._runId-BA-P6sGA.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");
5
5
  /**
6
6
  * Run detail PAGE — `/loops/$loopId/runs/$runId`. A standalone page (the trailing
7
7
  * `_` on the `$loopId` segment opts it out of the loop page's component so the run
@@ -9,19 +9,12 @@ var $$splitComponentImporter = () => import("./loops._loopId_.runs._runId-BA-P6s
9
9
  * modal or an inline panel. It resolves the run from the loop's detail payload
10
10
  * (reusing getLoopDetail) and the existing artifact diff.
11
11
  *
12
- * Auth-gated like the dashboard (`/`) so a logged-out/expired DEEP LINK shows the
13
- * sign-in CTA instead of a raw `loop not found` error from the blocked fetch.
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.
14
14
  */
15
15
  var Route = createFileRoute("/loops/$loopId_/runs/$runId")({
16
16
  ssr: false,
17
- loader: async () => {
18
- const auth = await getAuthState();
19
- if (auth.enabled) {
20
- const { data: session } = await authClient.getSession();
21
- if (!session) return { auth };
22
- }
23
- return { auth };
24
- },
17
+ loader: async () => ({ auth: await getAuthState() }),
25
18
  component: lazyRouteComponent($$splitComponentImporter, "component")
26
19
  });
27
20
  //#endregion
@@ -1,12 +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 { E as useSession, T as stopRun, _ as loadOlderRuns, m as getRunDiff, p as getLoopDetail } from "./SocialLinks-DVCLmql0.mjs";
5
- import { S as runPulseStyle, a as btn, g as humanBytes, h as fnum, i as StatusPill, m as fmt, n as Loading, o as btnDanger, p as dur, r as Pill, w as sectionHeadCls } from "./ui-DGz-q9MT.mjs";
6
- import { t as SignIn } from "./SignIn-BhoZIOUp.mjs";
7
- import { r as LoadErrorCard } from "./actionUi-Cf6DuEC2.mjs";
8
- import { t as Route } from "./loops._loopId_.runs._runId-DVtG0xYb.mjs";
9
- //#region node_modules/.nitro/vite/services/ssr/assets/loops._loopId_.runs._runId-BA-P6sGA.js
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
10
9
  var import_react = /* @__PURE__ */ __toESM(require_react());
11
10
  var import_jsx_runtime = require_jsx_runtime();
12
11
  /**
@@ -596,30 +595,39 @@ function RunDetailView({ loopId, runId }) {
596
595
  * modal or an inline panel. It resolves the run from the loop's detail payload
597
596
  * (reusing getLoopDetail) and the existing artifact diff.
598
597
  *
599
- * Auth-gated like the dashboard (`/`) so a logged-out/expired DEEP LINK shows the
600
- * sign-in CTA instead of a raw `loop not found` error from the blocked fetch.
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.
601
600
  */
602
601
  function RunDetailPage() {
603
602
  const { loopId, runId } = Route.useParams();
604
603
  const { auth } = Route.useLoaderData() ?? { auth: { enabled: false } };
605
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}` });
606
610
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", {
607
- className: "mx-auto max-w-[1360px] px-8 pb-24 pt-10",
608
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
609
- className: "mb-5",
610
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Link, {
611
- to: "/loops/$loopId",
612
- params: { loopId },
613
- className: "inline-flex items-center gap-1.5 text-label font-medium text-secondary transition-colors hover:text-display",
614
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
615
- "aria-hidden": true,
616
- children: "←"
617
- }), " Back to loop"]
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
618
629
  })
619
- }), auth?.enabled && !isPending && !session ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SignIn, { callbackURL: `/loops/${loopId}/runs/${runId}` }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunDetailView, {
620
- loopId,
621
- runId
622
- })]
630
+ ]
623
631
  });
624
632
  }
625
633
  //#endregion
@@ -1,31 +1,16 @@
1
- import { A as ensureServer, Bt as sha256, C as createMachine$1, G as getMachine, Jt as updateMachine, P as forceDeleteMachine, St as mintDeviceToken, T as daemonNeedsUpdate, X as getTeamMember, bt as machineIdFromToken, dt as listMachines$1, ft as listMachinesForTeam, jt as rememberConnectKey, u as MIN_DAEMON_VERSION, yt as loopsForMachine } from "./boot-BIxtZHU-.mjs";
2
- import { requestScope } from "./auth-LwPp_iim.mjs";
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";
3
4
  import { n as createServerFn } from "./ssr.mjs";
4
5
  import { t as createServerRpc } from "./createServerRpc-BD8Xn_0_.mjs";
5
- //#region node_modules/.nitro/vite/services/ssr/assets/machineFns-Bb9KNXun.js
6
- /**
7
- * Whether a machine is visible/actionable in the request scope. Mirrors
8
- * listMachines: open mode sees everything; otherwise the requester must own the
9
- * machine or share the active team with its owner. `teamMachineIds` is a THUNK
10
- * (the team set needs a DB join) invoked only after the owner check misses — the
11
- * hot path (machineStatus polls every ~2.5s while the connect dialog is open,
12
- * usually by the owner) pays no query.
13
- */
14
- function machineInScope(m, scope, teamMachineIds) {
15
- if (!scope.enforce) return true;
16
- if (!scope.userId) return false;
17
- if (m.userId === scope.userId) return true;
18
- return teamMachineIds().has(m.id);
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;
19
10
  }
20
- /**
21
- * Whether the machine's PLAINTEXT device token may be serialized to this
22
- * requester. Owner-only under the gate — the token fully impersonates the
23
- * machine, so even a teammate/admin who may see (or delete) the row gets
24
- * `token: null`. Open mode keeps the v1 behavior (token shown, e.g. for the
25
- * reconnect command).
26
- */
27
- function tokenVisibleTo(m, scope) {
28
- return !scope.enforce || m.userId === scope.userId;
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);
29
14
  }
30
15
  /**
31
16
  * Latest published @kky42/pievo version — a cached, fail-silent lookup of the
@@ -108,102 +93,64 @@ var LatestDaemonVersion = class {
108
93
  };
109
94
  var latestDaemonVersion = new LatestDaemonVersion();
110
95
  /**
111
- * Machine management server functions. Connect flow (two acts):
112
- * 1. createMachine() → mint a token, create a PENDING (unnamed) row, return
113
- * {id, token}. The UI shows the connect command + "waiting…".
114
- * 2. the daemon connects → poll records online + hostname/platform/arch. The
115
- * UI polls machineStatus(id); once connected it shows the name field
116
- * (prefilled with the hostname). finalizeMachine({id,name}) names it →
117
- * it appears in the list. cancelMachine(id) drops a pending row.
118
- *
119
- * Scoping: when the auth gate is on, every fn requires a signed-in user, and a
120
- * machine is visible/actionable only when the requester owns it, its owner is a
121
- * member of the active team, or the scope is the admin "All teams" view (the
122
- * same rule listMachines filters by). The PLAINTEXT device token is serialized
123
- * ONLY to the machine's owner — it fully impersonates the machine (poll /
124
- * create-loop / log), so a teammate who can see the row must never exfiltrate
125
- * it. Open mode (gate off) keeps the shared workspace: everyone sees/manages
126
- * all machines, tokens included. No workdir jail (fully open) for now.
127
- */
128
- /** Ids of the machines membership-visible in the scope's active team (the same
129
- * set listMachines serves). Empty when the scope needs no team check (open
130
- * mode or signed-out — machineInScope settles those before ever invoking its
131
- * team-set thunk). */
132
- async function teamMachineIds(scope) {
133
- if (!scope.enforce || !scope.userId) return /* @__PURE__ */ new Set();
134
- return new Set((await listMachinesForTeam(scope.teamId)).map((m) => m.id));
135
- }
136
- /**
137
- * Resolve a machine and authorize it against the request scope (the machine-fn
138
- * twin of loopApi's `ownedLoop`). `machine` is undefined when it's missing OR
139
- * out of scope — callers treat both as "not found" so existence never leaks.
140
- * The scope rides along so callers don't re-run requestScope (a second session
141
- * decrypt); the team-set thunk keeps the owner fast path query-free.
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.
142
99
  */
100
+ /** Missing and out-of-scope machines both resolve to undefined. */
143
101
  async function scopedMachine(id) {
144
102
  const scope = await requestScope();
145
- const m = await getMachine(id);
146
- const teamIds = await teamMachineIds(scope);
103
+ if (scope.enforce && !scope.userId) return { scope };
104
+ const candidate = await getMachine(id);
147
105
  return {
148
106
  scope,
149
- machine: m && machineInScope(m, scope, () => teamIds) ? m : void 0
107
+ machine: candidate && machineInScope(candidate, scope) ? candidate : void 0
150
108
  };
151
109
  }
152
- async function toSummary(m, scope) {
110
+ async function toSummary(machine, scope) {
153
111
  return {
154
- id: m.id,
155
- name: m.name,
156
- online: !!m.online,
157
- lastSeen: m.lastSeen ?? null,
158
- hostname: m.hostname ?? null,
159
- platform: m.platform ?? null,
160
- arch: m.arch ?? null,
161
- daemonVersion: m.daemonVersion ?? null,
162
- daemonProtocol: m.daemonProtocol ?? null,
112
+ id: machine.id,
113
+ name: machine.name,
114
+ online: !!machine.online,
115
+ lastSeen: machine.lastSeen ?? null,
116
+ hostname: machine.hostname ?? null,
117
+ platform: machine.platform ?? null,
118
+ arch: machine.arch ?? null,
119
+ daemonVersion: machine.daemonVersion ?? null,
120
+ daemonProtocol: machine.daemonProtocol ?? null,
163
121
  latestDaemonVersion: latestDaemonVersion.get(),
164
- needsUpdate: daemonNeedsUpdate(m.daemonVersion),
122
+ needsUpdate: daemonNeedsUpdate(machine.daemonVersion),
165
123
  requiredDaemonVersion: MIN_DAEMON_VERSION,
166
- token: tokenVisibleTo(m, scope) ? m.token ?? null : null,
167
- loopCount: (await loopsForMachine(m.id)).length
124
+ token: tokenVisibleTo(machine, scope) ? machine.token ?? null : null,
125
+ loopCount: (await loopsForMachine(machine.id)).length
168
126
  };
169
127
  }
170
- /** Named machines only (pending/unnamed rows are mid-connect). Scoped to the
171
- * given/active team when the gate is on; the full shared list in open mode. An
172
- * explicit `teamId` (the `/t/<id>` route) scopes this request independent of the
173
- * cookie, so a tab on /t/A and one on /t/B list different machines at once. */
174
128
  var listMachines_createServerFn_handler = createServerRpc({
175
129
  id: "aa46310016bb8a58b273a648ffd498c87f1f450d12827e30110a3b83ba6a27d9",
176
130
  name: "listMachines",
177
131
  filename: "src/server/machineFns.ts"
178
132
  }, (opts) => listMachines.__executeServer(opts));
179
- var listMachines = createServerFn({ method: "GET" }).validator((teamId) => teamId).handler(listMachines_createServerFn_handler, async ({ data: teamId }) => {
133
+ var listMachines = createServerFn({ method: "GET" }).handler(listMachines_createServerFn_handler, async () => {
180
134
  await ensureServer();
181
- const scope = await requestScope(teamId);
182
- const { enforce, userId, teamId: active } = scope;
183
- if (enforce && !userId) return [];
184
- const list = enforce ? await listMachinesForTeam(active) : await listMachines$1();
185
- return Promise.all(list.filter((m) => m.name.trim()).map((m) => toSummary(m, scope)));
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)));
186
139
  });
187
140
  var createMachine_createServerFn_handler = createServerRpc({
188
141
  id: "ed679b4e1e05e903a5d4ba4a70d9b9d883991ab22ed59b4d2d69078941818415",
189
142
  name: "createMachine",
190
143
  filename: "src/server/machineFns.ts"
191
144
  }, (opts) => createMachine.__executeServer(opts));
192
- var createMachine = createServerFn({ method: "POST" }).validator((teamId) => teamId).handler(createMachine_createServerFn_handler, async ({ data: teamId }) => {
145
+ var createMachine = createServerFn({ method: "POST" }).handler(createMachine_createServerFn_handler, async () => {
193
146
  await ensureServer();
194
- const { enforce, userId, teamId: active } = await requestScope(teamId);
195
- if (enforce && !userId) return { error: "not signed in" };
147
+ const scope = await requestScope();
148
+ if (scope.enforce && !scope.userId) return { error: "not signed in" };
196
149
  const token = mintDeviceToken();
197
150
  const id = machineIdFromToken(token);
198
- const owner = userId ?? "shared";
199
- await rememberConnectKey(token, {
200
- userId: owner,
201
- teamId: active
202
- });
203
151
  await createMachine$1({
204
152
  id,
205
- userId: owner,
206
- teamId: active,
153
+ userId: scope.userId ?? "shared",
207
154
  name: "",
208
155
  tokenHash: sha256(token),
209
156
  token,
@@ -229,7 +176,7 @@ var finalizeMachine_createServerFn_handler = createServerRpc({
229
176
  name: "finalizeMachine",
230
177
  filename: "src/server/machineFns.ts"
231
178
  }, (opts) => finalizeMachine.__executeServer(opts));
232
- var finalizeMachine = createServerFn({ method: "POST" }).validator((d) => d).handler(finalizeMachine_createServerFn_handler, async ({ data }) => {
179
+ var finalizeMachine = createServerFn({ method: "POST" }).validator((data) => data).handler(finalizeMachine_createServerFn_handler, async ({ data }) => {
233
180
  await ensureServer();
234
181
  const name = data.name?.trim();
235
182
  if (!name) return { ok: false };
@@ -253,21 +200,8 @@ var deleteMachine = createServerFn({ method: "POST" }).validator((id) => id).han
253
200
  ok: false,
254
201
  error: "machine not found"
255
202
  };
256
- const loops = await loopsForMachine(id);
257
- let allowedTeamIds = scope.enforce ? [] : void 0;
258
- if (scope.enforce && loops.length > 0) {
259
- allowedTeamIds = [...new Set(loops.map((loop) => loop.teamId))];
260
- if ((await Promise.all(allowedTeamIds.map((teamId) => getTeamMember(teamId, scope.userId)))).some((membership) => membership?.role !== "owner")) return {
261
- ok: false,
262
- error: "Only an owner of every affected team can delete this machine and its loops."
263
- };
264
- }
265
- const deleted = await forceDeleteMachine(id, allowedTeamIds);
266
- if (deleted.state === "forbidden") return {
267
- ok: false,
268
- error: "Machine loops changed while deleting; review them and try again."
269
- };
270
- if (deleted.state === "not-found") return {
203
+ const deleted = scope.enforce ? await forceDeleteMachine(id, scope.userId) : await forceDeleteMachine(id);
204
+ if (deleted.state === "forbidden" || deleted.state === "not-found") return {
271
205
  ok: false,
272
206
  error: "machine not found"
273
207
  };
@@ -1,6 +1,6 @@
1
- import { n as DefaultQueryCompiler, o as CompiledQuery, s as sql$1 } from "./auth-LwPp_iim.mjs";
1
+ import { n as DefaultQueryCompiler, o as CompiledQuery, s as sql$1 } from "./auth-0AamrwHo.mjs";
2
2
  import { n as DEFAULT_MIGRATION_TABLE, t as DEFAULT_MIGRATION_LOCK_TABLE } from "./kysely-migration-tables-JkVUjPF_-bPAX9LR_.mjs";
3
- //#region node_modules/.nitro/vite/services/ssr/assets/node-sqlite-dialect-Dho9YjXb.js
3
+ //#region node_modules/.nitro/vite/services/ssr/assets/node-sqlite-dialect-BbUqnnYv.js
4
4
  var NodeSqliteAdapter = class {
5
5
  get supportsCreateIfNotExists() {
6
6
  return true;