@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
@@ -0,0 +1,355 @@
1
+ import { d as validOptionalPollString, f as validPollCurrentRun, l as readByteBody, n as POLL_HTTP_BODY_FIELDS, p as validPollWireId, s as ensureServer, u as readJsonBody } from "./boot-DmpKtb0A.mjs";
2
+ import { h as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
3
+ import { c as createFileRoute, i as HeadContent, l as createRootRoute, o as createRouter, r as Scripts } from "../_libs/@tanstack/react-router+[...].mjs";
4
+ import { r as imageMime, t as Route$9 } from "./artifactKind-a2oxT2wQ.mjs";
5
+ import { t as Route$10 } from "./loops._loopId_.runs._runId-Dr6E-FA5.mjs";
6
+ import { n as Route$11 } from "./routes-CL2Kef-Q.mjs";
7
+ //#region node_modules/.nitro/vite/services/ssr/assets/router-BhHsTGqu.js
8
+ var import_jsx_runtime = require_jsx_runtime();
9
+ var app_default = "/assets/app-x1CeeY7N.css";
10
+ var Route$8 = createRootRoute({
11
+ head: () => ({
12
+ meta: [
13
+ { charSet: "utf-8" },
14
+ {
15
+ name: "viewport",
16
+ content: "width=device-width, initial-scale=1"
17
+ },
18
+ { title: "Pievo" }
19
+ ],
20
+ links: [{
21
+ rel: "stylesheet",
22
+ href: app_default
23
+ }, {
24
+ rel: "icon",
25
+ type: "image/svg+xml",
26
+ href: "/favicon.svg"
27
+ }]
28
+ }),
29
+ shellComponent: RootDocument
30
+ });
31
+ function RootDocument({ children }) {
32
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("html", {
33
+ lang: "en",
34
+ suppressHydrationWarning: true,
35
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("head", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HeadContent, {}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("body", {
36
+ className: "dot-grid-subtle bg-paper font-sans text-primary antialiased",
37
+ suppressHydrationWarning: true,
38
+ children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Scripts, {})]
39
+ })]
40
+ });
41
+ }
42
+ async function machineJsonRoute(request, options) {
43
+ const auth = request.headers.get("authorization") ?? "";
44
+ const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
45
+ if (!token) return Response.json({ error: options.missingCredential }, { status: 401 });
46
+ const parsed = await readJsonBody(request, options.maxBytes ?? 2097152);
47
+ if (parsed.kind === "too-large") return Response.json({ error: "body too large" }, { status: 413 });
48
+ if (parsed.kind !== "ok") {
49
+ if (options.invalidBody === void 0) return Response.json({ error: options.invalidJsonError ?? "invalid JSON body" }, { status: 400 });
50
+ return options.handle(token, options.invalidBody);
51
+ }
52
+ const body = options.parseBody(parsed.body);
53
+ if (body === null) return Response.json({ error: options.invalidBodyError ?? "invalid request body" }, { status: 400 });
54
+ return options.handle(token, body);
55
+ }
56
+ var Route$7 = createFileRoute("/machine/report")({ server: { handlers: { POST: async ({ request }) => {
57
+ return machineJsonRoute(request, {
58
+ missingCredential: "missing token",
59
+ invalidBody: {},
60
+ parseBody: (value) => value && typeof value === "object" ? value : {},
61
+ handle: async (token, body) => {
62
+ const { getGateway } = await import("./boot-DmpKtb0A.mjs").then((n) => n.r).then((n) => n.t);
63
+ const r = await (await getGateway()).report(token, body);
64
+ return Response.json(r.body, { status: r.status });
65
+ }
66
+ });
67
+ } } } });
68
+ /**
69
+ * Deep readiness for process launchers. Unlike /api/health, this waits for the
70
+ * one-time backend boot (migrations, blob-store selection, and scheduler start).
71
+ * The per-launch nonce lets a launcher reject a different process already bound
72
+ * to the requested port.
73
+ */
74
+ async function readyResponse(ensure = ensureServer, env = process.env) {
75
+ await ensure();
76
+ return Response.json({
77
+ ok: true,
78
+ nonce: env.PIEVO_SERVER_LAUNCH_NONCE ?? null
79
+ });
80
+ }
81
+ var Route$6 = createFileRoute("/api/ready")({ server: { handlers: { GET: () => readyResponse() } } });
82
+ var Route$5 = createFileRoute("/api/health")({ server: { handlers: { GET: async () => Response.json({ ok: true }) } } });
83
+ var Route$4 = createFileRoute("/api/machine/status")({ server: { handlers: { GET: async ({ request }) => {
84
+ const auth = request.headers.get("authorization") ?? "";
85
+ const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
86
+ if (!token) return Response.json({ error: "missing device token" }, { status: 401 });
87
+ const { getGateway } = await import("./boot-DmpKtb0A.mjs").then((n) => n.r).then((n) => n.t);
88
+ const r = await (await getGateway()).status(token);
89
+ return Response.json(r.body, { status: r.status });
90
+ } } } });
91
+ /** Reject non-object bodies, additions, coercible types, over-cap strings, and
92
+ * NUL-bearing text before the gateway is loaded or any state can mutate. */
93
+ function parsePollBody(value) {
94
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
95
+ if (Object.keys(value).some((key) => !POLL_HTTP_BODY_FIELDS.has(key))) return null;
96
+ const body = value;
97
+ if (!validOptionalPollString(body.host, 524288) || !validOptionalPollString(body.platform, 524288) || !validOptionalPollString(body.arch, 524288) || !validOptionalPollString(body.version, 64)) return null;
98
+ if (body.protocolVersion !== void 0 && (typeof body.protocolVersion !== "number" || !Number.isInteger(body.protocolVersion))) return null;
99
+ if (body.currentRuns !== void 0 && (!Array.isArray(body.currentRuns) || body.currentRuns.some((run) => !validPollCurrentRun(run)))) return null;
100
+ if (body.daemonInstanceId !== void 0 && !validPollWireId(body.daemonInstanceId)) return null;
101
+ if (body.recoveryComplete !== void 0 && typeof body.recoveryComplete !== "boolean") return null;
102
+ return body;
103
+ }
104
+ function gatewayPollRequest(body) {
105
+ return {
106
+ protocolVersion: body.protocolVersion,
107
+ currentRuns: body.currentRuns,
108
+ daemonInstanceId: body.daemonInstanceId,
109
+ recoveryComplete: body.recoveryComplete,
110
+ info: {
111
+ host: body.host,
112
+ platform: body.platform,
113
+ arch: body.arch,
114
+ version: body.version
115
+ }
116
+ };
117
+ }
118
+ var Route$3 = createFileRoute("/api/machine/poll")({ server: { handlers: { POST: async ({ request }) => {
119
+ return machineJsonRoute(request, {
120
+ missingCredential: "missing device token",
121
+ invalidBodyError: "unknown or invalid poll body fields",
122
+ invalidJsonError: "invalid JSON body",
123
+ parseBody: parsePollBody,
124
+ handle: async (token, body) => {
125
+ const { getGateway } = await import("./boot-DmpKtb0A.mjs").then((n) => n.r).then((n) => n.t);
126
+ const r = await (await getGateway()).pollV4Wait(token, gatewayPollRequest(body));
127
+ return Response.json(r.body, { status: r.status });
128
+ }
129
+ });
130
+ } } } });
131
+ function parseCliBody(value) {
132
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
133
+ const body = value;
134
+ if (Object.keys(body).length !== 1 || !Object.prototype.hasOwnProperty.call(body, "argv")) return null;
135
+ if (!Array.isArray(body.argv) || body.argv.some((entry) => typeof entry !== "string")) return null;
136
+ return { argv: body.argv };
137
+ }
138
+ /**
139
+ * POST /api/machine/cli — the ONE unified CLI dispatch (Bearer credential + `{argv}`).
140
+ * `CliGateway.cli()` branches by credential type: a `dk_` device token takes owner
141
+ * commands; an `rk_` run credential is rejected. The 4 MB body cap fits
142
+ * `file set` carrying a 2 MB artifact as base64 (4/3 expansion) plus argv framing.
143
+ */
144
+ var CLI_BODY_CAP = 4 * 1024 * 1024;
145
+ var Route$2 = createFileRoute("/api/machine/cli")({ server: { handlers: { POST: async ({ request }) => {
146
+ return machineJsonRoute(request, {
147
+ missingCredential: "missing credential",
148
+ maxBytes: CLI_BODY_CAP,
149
+ invalidJsonError: "body must be exactly {argv:string[]}",
150
+ invalidBodyError: "body must be exactly {argv:string[]}",
151
+ parseBody: parseCliBody,
152
+ handle: async (token, body) => {
153
+ const { getCliGateway } = await import("./boot-DmpKtb0A.mjs").then((n) => n.r).then((n) => n.t);
154
+ const r = await (await getCliGateway()).cli(token, body.argv);
155
+ return Response.json(r.body, { status: r.status });
156
+ }
157
+ });
158
+ } } } });
159
+ /**
160
+ * `decodeURIComponent` that returns null on malformed percent-encoding instead
161
+ * of throwing a URIError (which would escape a route handler as a 500). Shared
162
+ * by the routes that decode path segments; the policy is uniform — ANY
163
+ * malformed segment is the caller's clean 400, never silently kept raw.
164
+ */
165
+ function safeDecode(s) {
166
+ try {
167
+ return decodeURIComponent(s);
168
+ } catch {
169
+ return null;
170
+ }
171
+ }
172
+ /**
173
+ * GET|PUT /api/machine/run-files/:name — loop-file transfer for one run (Bearer
174
+ * RUN token). GET returns the version the run starts from, with its sha256 in
175
+ * `x-pievo-sha256`; PUT stores a version the run produced (the daemon sends the
176
+ * expected sha256 in the same header). A PUT never changes the loop: only the
177
+ * terminal report applies stored versions, under the loop lock.
178
+ *
179
+ * Not rate limited: a live run token is required and every body is bounded by
180
+ * the per-file loop-file limit.
181
+ */
182
+ function bearer(request) {
183
+ const auth = request.headers.get("authorization") ?? "";
184
+ return auth.startsWith("Bearer ") ? auth.slice(7) : "";
185
+ }
186
+ function fileName(request) {
187
+ return safeDecode(new URL(request.url).pathname.split("/").pop() ?? "");
188
+ }
189
+ var Route$1 = createFileRoute("/api/machine/run-files/$name")({ server: { handlers: {
190
+ GET: async ({ request }) => {
191
+ const token = bearer(request);
192
+ if (!token) return Response.json({ error: "missing run token" }, { status: 401 });
193
+ const name = fileName(request);
194
+ if (name === null) return Response.json({ error: "bad file name" }, { status: 400 });
195
+ const { getGateway } = await import("./boot-DmpKtb0A.mjs").then((n) => n.r).then((n) => n.t);
196
+ const r = await (await getGateway()).readRunFile(token, name);
197
+ if (r.status !== 200 || !r.bytes || !r.hash) return Response.json(r.body, { status: r.status });
198
+ return new Response(new Uint8Array(r.bytes), {
199
+ status: 200,
200
+ headers: {
201
+ "content-type": "application/octet-stream",
202
+ "content-length": String(r.bytes.length),
203
+ "x-pievo-sha256": r.hash,
204
+ "cache-control": "private, no-store"
205
+ }
206
+ });
207
+ },
208
+ PUT: async ({ request }) => {
209
+ const token = bearer(request);
210
+ if (!token) return Response.json({ error: "missing run token" }, { status: 401 });
211
+ const name = fileName(request);
212
+ if (name === null) return Response.json({ error: "bad file name" }, { status: 400 });
213
+ const { LOOP_FILE_BYTE_CAP } = await import("./boot-DmpKtb0A.mjs").then((n) => n.M).then((n) => n.Vt);
214
+ const body = await readByteBody(request, LOOP_FILE_BYTE_CAP);
215
+ if (body.kind === "too-large") return Response.json({ error: "file exceeds the loop-file size limit" }, { status: 413 });
216
+ if (body.kind === "invalid") return Response.json({ error: "could not read file body" }, { status: 400 });
217
+ const bytes = Buffer.from(body.bytes.buffer, body.bytes.byteOffset, body.bytes.byteLength);
218
+ const { getGateway } = await import("./boot-DmpKtb0A.mjs").then((n) => n.r).then((n) => n.t);
219
+ const r = await (await getGateway()).storeRunFile(token, name, bytes, request.headers.get("x-pievo-sha256"));
220
+ return Response.json(r.body, { status: r.status });
221
+ }
222
+ } } });
223
+ /**
224
+ * GET /api/artifact/:loopId/:path - stream one of a loop's file blobs.
225
+ * Two dispositions:
226
+ * - default: `attachment` (the download affordance for binary/oversize files;
227
+ * text is shown inline via getArtifact).
228
+ * - `?view=inline`: for a KNOWN image type only (the `imageMime` allowlist),
229
+ * served `inline` with the real image content-type so an <img> can render it.
230
+ * Hardened against a direct navigation turning the byte stream into a scripted
231
+ * document on the app origin: `X-Content-Type-Options: nosniff` pins the type
232
+ * and `Content-Security-Policy: sandbox` loads it in an opaque origin with no
233
+ * scripts - so an inline SVG can't run script against the session even if the
234
+ * URL is opened directly. Non-image inline requests fall back to attachment.
235
+ *
236
+ * Served to dashboard callers like the server functions. The path is matched
237
+ * exactly against a loop's stored file rows, and blob keys are content hashes,
238
+ * never the user path. Reads stream from the local BlobStore; the route never writes.
239
+ */
240
+ var PREFIX = "/api/artifact/";
241
+ var Route = createFileRoute("/api/artifact/$loopId/$")({ server: { handlers: { GET: async ({ request }) => {
242
+ const pathname = new URL(request.url).pathname;
243
+ if (!pathname.startsWith(PREFIX)) return Response.json({ error: "not found" }, { status: 404 });
244
+ const after = pathname.slice(14);
245
+ const slash = after.indexOf("/");
246
+ if (slash < 0) return Response.json({ error: "missing path" }, { status: 400 });
247
+ const loopId = safeDecode(after.slice(0, slash));
248
+ if (loopId === null) return Response.json({ error: "bad loop id" }, { status: 400 });
249
+ const segments = after.slice(slash + 1).split("/").map((s) => safeDecode(s));
250
+ if (segments.some((s) => s === null)) return Response.json({ error: "bad path" }, { status: 400 });
251
+ const relPath = segments.join("/");
252
+ if (!await (await import("./boot-DmpKtb0A.mjs").then((n) => n.M).then((n) => n.Dt)).getLoop(loopId)) return Response.json({ error: "not found" }, { status: 404 });
253
+ const { readLoopArtifactBytes } = await import("./artifactFiles-B0NPRDOR.mjs");
254
+ const r = await readLoopArtifactBytes(loopId, relPath);
255
+ if (r.status !== 200 || !r.bytes) return Response.json({ error: "not found" }, { status: r.status });
256
+ const filename = (r.filename || "file").replace(/["\\\r\n]/g, "_");
257
+ const mime = new URL(request.url).searchParams.get("view") === "inline" ? imageMime(relPath) : null;
258
+ if (mime) return new Response(new Uint8Array(r.bytes), {
259
+ status: 200,
260
+ headers: {
261
+ "content-type": mime,
262
+ "content-length": String(r.bytes.length),
263
+ "content-disposition": `inline; filename="${filename}"`,
264
+ "x-content-type-options": "nosniff",
265
+ "content-security-policy": "sandbox",
266
+ "cache-control": "private, no-store"
267
+ }
268
+ });
269
+ return new Response(new Uint8Array(r.bytes), {
270
+ status: 200,
271
+ headers: {
272
+ "content-type": r.binary ? "application/octet-stream" : "text/plain; charset=utf-8",
273
+ "content-length": String(r.bytes.length),
274
+ "content-disposition": `attachment; filename="${filename}"`,
275
+ "cache-control": "private, no-store"
276
+ }
277
+ });
278
+ } } } });
279
+ var IndexRoute = Route$11.update({
280
+ id: "/",
281
+ path: "/",
282
+ getParentRoute: () => Route$8
283
+ });
284
+ var MachineReportRoute = Route$7.update({
285
+ id: "/machine/report",
286
+ path: "/machine/report",
287
+ getParentRoute: () => Route$8
288
+ });
289
+ var LoopsLoopIdRoute = Route$9.update({
290
+ id: "/loops/$loopId",
291
+ path: "/loops/$loopId",
292
+ getParentRoute: () => Route$8
293
+ });
294
+ var ApiReadyRoute = Route$6.update({
295
+ id: "/api/ready",
296
+ path: "/api/ready",
297
+ getParentRoute: () => Route$8
298
+ });
299
+ var ApiHealthRoute = Route$5.update({
300
+ id: "/api/health",
301
+ path: "/api/health",
302
+ getParentRoute: () => Route$8
303
+ });
304
+ var ApiMachineStatusRoute = Route$4.update({
305
+ id: "/api/machine/status",
306
+ path: "/api/machine/status",
307
+ getParentRoute: () => Route$8
308
+ });
309
+ var ApiMachinePollRoute = Route$3.update({
310
+ id: "/api/machine/poll",
311
+ path: "/api/machine/poll",
312
+ getParentRoute: () => Route$8
313
+ });
314
+ var ApiMachineCliRoute = Route$2.update({
315
+ id: "/api/machine/cli",
316
+ path: "/api/machine/cli",
317
+ getParentRoute: () => Route$8
318
+ });
319
+ var LoopsLoopIdRunsRunIdRoute = Route$10.update({
320
+ id: "/loops/$loopId_/runs/$runId",
321
+ path: "/loops/$loopId/runs/$runId",
322
+ getParentRoute: () => Route$8
323
+ });
324
+ var ApiMachineRunFilesNameRoute = Route$1.update({
325
+ id: "/api/machine/run-files/$name",
326
+ path: "/api/machine/run-files/$name",
327
+ getParentRoute: () => Route$8
328
+ });
329
+ var rootRouteChildren = {
330
+ IndexRoute,
331
+ ApiHealthRoute,
332
+ ApiReadyRoute,
333
+ LoopsLoopIdRoute,
334
+ MachineReportRoute,
335
+ ApiMachineCliRoute,
336
+ ApiMachinePollRoute,
337
+ ApiMachineStatusRoute,
338
+ ApiArtifactLoopIdSplatRoute: Route.update({
339
+ id: "/api/artifact/$loopId/$",
340
+ path: "/api/artifact/$loopId/$",
341
+ getParentRoute: () => Route$8
342
+ }),
343
+ ApiMachineRunFilesNameRoute,
344
+ LoopsLoopIdRunsRunIdRoute
345
+ };
346
+ var routeTree = Route$8._addFileChildren(rootRouteChildren)._addFileTypes();
347
+ function getRouter() {
348
+ return createRouter({
349
+ routeTree,
350
+ scrollRestoration: true,
351
+ defaultPreload: "intent"
352
+ });
353
+ }
354
+ //#endregion
355
+ export { getRouter };
@@ -0,0 +1,12 @@
1
+ import { h as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
2
+ import { n as Route, t as DashboardView } from "./routes-CL2Kef-Q.mjs";
3
+ //#region node_modules/.nitro/vite/services/ssr/assets/routes-CCallyCT.js
4
+ var import_jsx_runtime = require_jsx_runtime();
5
+ /** First-load failure screen — a calm retry instead of the router's default
6
+ * error dump. Only the initial loader can land here; the in-page poll is
7
+ * fetch-then-set and keeps stale data on a transient blip. */
8
+ function Home() {
9
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DashboardView, { initial: Route.useLoaderData() });
10
+ }
11
+ //#endregion
12
+ export { Home as component };
@@ -1,10 +1,10 @@
1
1
  import { o as __toESM } from "../_runtime.mjs";
2
2
  import { g as require_react, h as require_jsx_runtime, s as TooltipProvider } from "../_libs/@base-ui/react+[...].mjs";
3
- import { c as createFileRoute, d as useNavigate, f as useRouter, s as lazyRouteComponent } from "../_libs/@tanstack/react-router+[...].mjs";
4
- import { O as useSession, _ as listLoops, a as PievoLogo, f as getAuthState, i as OpenModeWarning, n as GITHUB_URL, p as getConfig, r as GitHubIcon, v as loadOlderRuns, w as signOut, y as mintConnectKey } from "./auth-client-lSMb_Idt.mjs";
5
- import { f as btnSm, y as focusRing } from "./actionUi-5HOCZnom.mjs";
6
- import { c as mergeRuns, i as ModalHead, n as MachinesModal, o as daemonConnectCommand, r as Modal, s as listMachines, t as LoopOverview } from "./MachinesModal-BaEtzycr.mjs";
7
- //#region node_modules/.nitro/vite/services/ssr/assets/routes-Cr-9yDEH.js
3
+ import { c as createFileRoute, d as useNavigate, s as lazyRouteComponent } from "../_libs/@tanstack/react-router+[...].mjs";
4
+ import { c as getConfig, f as listLoops, m as mintConnectKey, p as loadOlderRuns } from "./lifecycleUi-DEjM8Etm.mjs";
5
+ import { f as btnSm, y as focusRing } from "./actionUi-DE-Pb2tv.mjs";
6
+ import { c as listMachines, i as ModalHead, l as mergeRuns, n as MachinesModal, o as connectServerUrl, r as Modal, s as daemonConnectCommand, t as LoopOverview } from "./MachinesModal-CLBZxZkq.mjs";
7
+ //#region node_modules/.nitro/vite/services/ssr/assets/routes-CL2Kef-Q.js
8
8
  var import_react = /* @__PURE__ */ __toESM(require_react());
9
9
  var import_jsx_runtime = require_jsx_runtime();
10
10
  function LoopCard({ loop, onOpen, onPickRun }) {
@@ -81,7 +81,7 @@ function ComposeModal({ open, onClose }) {
81
81
  const [error, setError] = (0, import_react.useState)(null);
82
82
  const [configError, setConfigError] = (0, import_react.useState)(false);
83
83
  const origin = typeof window !== "undefined" ? window.location.origin : "";
84
- const command = token && config ? daemonConnectCommand(origin, token, config.pievoCli, config.customCli) : "";
84
+ const command = token && config ? daemonConnectCommand(connectServerUrl(origin, config.publicUrl), token, config.pievoCli, config.customCli) : "";
85
85
  (0, import_react.useEffect)(() => {
86
86
  if (!open) return;
87
87
  setToken(null);
@@ -99,9 +99,7 @@ function ComposeModal({ open, onClose }) {
99
99
  if (active) setConfigError(true);
100
100
  });
101
101
  mintConnectKey().then((result) => {
102
- if (!active) return;
103
- if ("token" in result) setToken(result.token);
104
- else setError(result.error);
102
+ if (active) setToken(result.token);
105
103
  }).catch(() => {
106
104
  if (active) setError("could not mint a connect key");
107
105
  });
@@ -206,6 +204,85 @@ function Step({ number, title, continued = false, children }) {
206
204
  })]
207
205
  });
208
206
  }
207
+ /**
208
+ * Shared Pievo mark geometry: eight durable steps form a cycle and the
209
+ * interactive-blue runner marks the next scheduled pass.
210
+ *
211
+ * Variants are props on one geometry (styles in app.css):
212
+ * - `plate` - dark tile behind the steps (favicon / app-icon contexts);
213
+ * without it the steps inherit currentColor (inline contexts).
214
+ * - `trail` - two fading steps behind the runner, so a static rendering
215
+ * still reads as motion (print / social cards).
216
+ * - `animated` - the runner advances around the cycle; reduced-motion users
217
+ * see the canonical static first frame.
218
+ */
219
+ var RING = [
220
+ [9, 9],
221
+ [37, 9],
222
+ [65, 9],
223
+ [65, 37],
224
+ [65, 65],
225
+ [37, 65],
226
+ [9, 65],
227
+ [9, 37]
228
+ ];
229
+ function RingLogo({ size = 24, plate = false, trail = false, animated = false }) {
230
+ const cellClass = (i) => {
231
+ const parts = ["ring-cell", `rc${i}`];
232
+ if (i === 0) parts.push("ring-runner");
233
+ else if (trail && i === 7) parts.push("ring-trail-1");
234
+ else if (trail && i === 6) parts.push("ring-trail-2");
235
+ return parts.join(" ");
236
+ };
237
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", {
238
+ width: size,
239
+ height: size,
240
+ viewBox: "0 0 96 96",
241
+ role: "img",
242
+ "aria-label": "Pievo",
243
+ className: [
244
+ "ring-logo",
245
+ plate && "ring-logo-plate",
246
+ animated && "ring-logo-animated"
247
+ ].filter(Boolean).join(" "),
248
+ children: [plate && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", {
249
+ width: "96",
250
+ height: "96",
251
+ rx: "15",
252
+ fill: "#0b0b0b"
253
+ }), RING.map(([x, y], i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", {
254
+ x,
255
+ y,
256
+ width: 22,
257
+ height: 22,
258
+ rx: 6,
259
+ className: cellClass(i)
260
+ }, i))]
261
+ });
262
+ }
263
+ /**
264
+ * Pievo's primary product mark: one scheduled pass moving around a durable ring.
265
+ * It reuses the same geometry as the favicon and README asset; animation is a
266
+ * progressive enhancement and parks on the canonical first frame when reduced
267
+ * motion is requested.
268
+ */
269
+ function PievoLogo({ size = 56 }) {
270
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RingLogo, {
271
+ size,
272
+ plate: true,
273
+ animated: true
274
+ });
275
+ }
276
+ var GITHUB_URL = "https://github.com/kky42/pievo";
277
+ function GitHubIcon({ className }) {
278
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
279
+ viewBox: "0 0 24 24",
280
+ fill: "currentColor",
281
+ "aria-hidden": true,
282
+ className,
283
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.11.79-.25.79-.55 0-.27-.01-1.17-.02-2.12-3.2.7-3.88-1.36-3.88-1.36-.52-1.33-1.28-1.68-1.28-1.68-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.19 1.76 1.19 1.03 1.75 2.69 1.25 3.34.95.1-.74.4-1.25.72-1.54-2.55-.29-5.24-1.28-5.24-5.68 0-1.26.45-2.28 1.19-3.09-.12-.29-.52-1.46.11-3.05 0 0 .97-.31 3.18 1.18a11.1 11.1 0 0 1 5.8 0c2.2-1.49 3.17-1.18 3.17-1.18.63 1.59.23 2.76.11 3.05.74.81 1.19 1.83 1.19 3.09 0 4.41-2.69 5.38-5.25 5.67.41.35.77 1.05.77 2.12 0 1.53-.01 2.76-.01 3.14 0 .3.2.66.8.55A11.51 11.51 0 0 0 23.5 12C23.5 5.65 18.35.5 12 .5z" })
284
+ });
285
+ }
209
286
  function loopFilterKey(filter) {
210
287
  return filter.kind === "tag" ? `tag:${filter.tag}` : filter.kind;
211
288
  }
@@ -260,8 +337,6 @@ function filterLoops(loops, filter) {
260
337
  case "tag": return loops.filter((loop) => loop.tags.includes(filter.tag));
261
338
  }
262
339
  }
263
- /** Loops and machines change between polls. Authorization is derived server-side
264
- * from the current session; clients do not send tenant identifiers. */
265
340
  async function fetchLiveData() {
266
341
  const [loops, machines] = await Promise.all([listLoops(), listMachines()]);
267
342
  return {
@@ -269,27 +344,14 @@ async function fetchLiveData() {
269
344
  machines
270
345
  };
271
346
  }
272
- function DashboardView({ initial, mode }) {
347
+ function DashboardView({ initial }) {
273
348
  const [live, setLive] = (0, import_react.useState)(null);
274
349
  const { loops, machines } = live ?? initial;
275
350
  const online = machines.filter((m) => m.online).length;
276
351
  const navigate = useNavigate();
277
- const router = useRouter();
278
- const { data: session } = useSession();
279
- const [signingOut, setSigningOut] = (0, import_react.useState)(false);
280
352
  const [composeOpen, setComposeOpen] = (0, import_react.useState)(false);
281
353
  const [machinesOpen, setMachinesOpen] = (0, import_react.useState)(false);
282
354
  const [filter, setFilter] = (0, import_react.useState)({ kind: "all" });
283
- async function logout() {
284
- setSigningOut(true);
285
- try {
286
- await signOut();
287
- await navigate({ to: "/" });
288
- await router.invalidate();
289
- } finally {
290
- setSigningOut(false);
291
- }
292
- }
293
355
  const refetch = (0, import_react.useCallback)(async () => {
294
356
  try {
295
357
  setLive(await fetchLiveData());
@@ -340,7 +402,7 @@ function DashboardView({ initial, mode }) {
340
402
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PievoLogo, { size: 30 })
341
403
  }),
342
404
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
343
- className: `shrink-0 text-[18px] font-semibold tracking-[-0.015em] text-display ${mode === "auth" ? "hidden sm:inline" : ""}`,
405
+ className: "shrink-0 text-[18px] font-semibold tracking-[-0.015em] text-display",
344
406
  children: "Pievo"
345
407
  }),
346
408
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1" }),
@@ -353,19 +415,6 @@ function DashboardView({ initial, mode }) {
353
415
  className: `${headerIconBtn} hidden sm:inline-flex`,
354
416
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitHubIcon, { className: "size-[17px]" })
355
417
  }),
356
- mode === "auth" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
357
- className: "flex shrink-0 items-center gap-2",
358
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
359
- className: "hidden max-w-48 truncate text-label text-secondary md:inline",
360
- title: session?.user.email ?? session?.user.name ?? "Account",
361
- children: session?.user.email ?? session?.user.name ?? "Account"
362
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
363
- onClick: () => void logout(),
364
- disabled: signingOut,
365
- className: headerBtn,
366
- children: signingOut ? "Signing out…" : "Sign out"
367
- })]
368
- }),
369
418
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
370
419
  onClick: () => setMachinesOpen(true),
371
420
  "aria-label": `${online} ${online === 1 ? "machine" : "machines"} online`,
@@ -398,7 +447,6 @@ function DashboardView({ initial, mode }) {
398
447
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", {
399
448
  className: "mx-auto max-w-[1180px] px-8 pb-24",
400
449
  children: [
401
- mode === "open" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OpenModeWarning, { className: "mt-6" }),
402
450
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
403
451
  className: "pb-2 pt-14 text-center",
404
452
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
@@ -472,18 +520,11 @@ function DashboardView({ initial, mode }) {
472
520
  }
473
521
  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";
474
522
  var headerIconBtn = "shrink-0 cursor-pointer items-center rounded-full p-1.5 text-secondary transition-colors hover:bg-raised hover:text-display";
475
- var $$splitErrorComponentImporter = () => import("./routes-DRBS4Zi4.mjs");
476
- var $$splitComponentImporter = () => import("./routes-DTBltabU.mjs");
477
- /** The dashboard route. Auth mode shows sign-in before loading owner-scoped data;
478
- * open mode renders the server's shared administrative workspace. */
523
+ var $$splitErrorComponentImporter = () => import("./routes-ngpX7h8D.mjs");
524
+ var $$splitComponentImporter = () => import("./routes-CCallyCT.mjs");
479
525
  var Route = createFileRoute("/")({
480
526
  ssr: false,
481
- loader: async () => {
482
- return {
483
- auth: await getAuthState(),
484
- initial: await fetchLiveData()
485
- };
486
- },
527
+ loader: () => fetchLiveData(),
487
528
  component: lazyRouteComponent($$splitComponentImporter, "component"),
488
529
  errorComponent: lazyRouteComponent($$splitErrorComponentImporter, "errorComponent")
489
530
  });
@@ -1,10 +1,8 @@
1
1
  import { h as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
2
2
  import { f as useRouter } from "../_libs/@tanstack/react-router+[...].mjs";
3
- import { i as LoadErrorCard } from "./actionUi-5HOCZnom.mjs";
4
- //#region node_modules/.nitro/vite/services/ssr/assets/routes-DRBS4Zi4.js
3
+ import { i as LoadErrorCard } from "./actionUi-DE-Pb2tv.mjs";
4
+ //#region node_modules/.nitro/vite/services/ssr/assets/routes-ngpX7h8D.js
5
5
  var import_jsx_runtime = require_jsx_runtime();
6
- /** The dashboard route. Auth mode shows sign-in before loading owner-scoped data;
7
- * open mode renders the server's shared administrative workspace. */
8
6
  /** First-load failure screen — a calm retry instead of the router's default
9
7
  * error dump. Only the initial loader can land here; the in-page poll is
10
8
  * fetch-then-set and keeps stale data on a transient blip. */