@kky42/pievo-server 2.4.14 → 3.0.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.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{actionUi-5PtES4q0.js → actionUi-BZrLpgHT.js} +1 -1
- package/.output/public/assets/app-DXLuQNb9.css +2 -0
- package/.output/public/assets/index-Di_wpAtL.js +10 -0
- package/.output/public/assets/loops._loopId-X3ClNPMY.js +67 -0
- package/.output/public/assets/loops._loopId_.runs._runId-ByU2BSyF.js +3 -0
- package/.output/public/assets/preload-helper-hRTYB_ju.js +3 -0
- package/.output/public/assets/{routes-Dm_3J2aV.js → routes-BIOJZsig.js} +1 -1
- package/.output/public/assets/routes-K8D5uBiV.js +1 -0
- package/.output/server/_chunks/ssr-renderer.mjs +1 -1
- package/.output/server/_libs/{drizzle-orm+postgres.mjs → drizzle-orm.mjs} +93 -2218
- package/.output/server/_ssr/{MachinesModal-BaEtzycr.mjs → MachinesModal-CmVMec6B.mjs} +46 -46
- package/.output/server/_ssr/{actionUi-5HOCZnom.mjs → actionUi-DE-Pb2tv.mjs} +1 -2
- package/.output/server/_ssr/{artifactFiles-DO43wwXA.mjs → artifactFiles-Di1xDKvX.mjs} +5 -8
- package/.output/server/_ssr/{artifactKind-DwDr8IYc.mjs → artifactKind-DqpmtxDU.mjs} +3 -9
- package/.output/server/_ssr/{boot-C472kLwY.mjs → boot-CYRi-wez.mjs} +2152 -351
- package/.output/server/_ssr/lifecycleUi-BsbUsi5K.mjs +106 -0
- package/.output/server/_ssr/{loopApi-DT751TJA.mjs → loopApi-Bez1rHV0.mjs} +50 -66
- package/.output/server/_ssr/{loopProjection-qaD2I48a.mjs → loopProjection-esCjyZJg.mjs} +4 -23
- package/.output/server/_ssr/{loops._loopId-CXgw2Rz1.mjs → loops._loopId-KUezNY69.mjs} +8 -23
- package/.output/server/_ssr/{loops._loopId_.runs._runId-Dk94Z0Jq.mjs → loops._loopId_.runs._runId-BFP9oMrp.mjs} +2 -7
- package/.output/server/_ssr/{loops._loopId_.runs._runId-SASSqOfy.mjs → loops._loopId_.runs._runId-DOkUEesG.mjs} +18 -33
- package/.output/server/_ssr/{machineFns-BSrIgn3f.mjs → machineFns-CniGcY8s.mjs} +13 -50
- package/.output/server/_ssr/networkAccess-CIlK3ION.mjs +30 -0
- package/.output/server/_ssr/router-BFIH2UAE.mjs +362 -0
- package/.output/server/_ssr/routes-BSVBANI3.mjs +12 -0
- package/.output/server/_ssr/{routes-Cr-9yDEH.mjs → routes-DnE7XDe_.mjs} +91 -50
- package/.output/server/_ssr/{routes-DRBS4Zi4.mjs → routes-ngpX7h8D.mjs} +2 -4
- package/.output/server/_ssr/ssr.mjs +25 -34
- package/.output/server/_ssr/start-B5_9Z5W-.mjs +35 -0
- package/.output/server/{_tanstack-start-manifest_v-DxLIQ0cc.mjs → _tanstack-start-manifest_v-DnwAupbZ.mjs} +8 -13
- package/.output/server/drizzle/0000_baseline.sql +6 -97
- package/.output/server/drizzle/meta/_journal.json +2 -23
- package/.output/server/index.mjs +45 -52
- package/drizzle/0000_baseline.sql +6 -97
- package/drizzle/meta/_journal.json +2 -23
- package/package.json +4 -8
- package/scripts/pievo-server.mjs +49 -47
- package/scripts/server-cli-lib.mjs +21 -12
- package/scripts/server-network.mjs +139 -0
- package/.output/public/assets/SignIn-CIFwnZtF.js +0 -1
- package/.output/public/assets/app-DvHMhs7S.css +0 -2
- package/.output/public/assets/index-CRX2pu0W.js +0 -10
- package/.output/public/assets/loops._loopId-BsuV-Rmf.js +0 -67
- package/.output/public/assets/loops._loopId_.runs._runId-DpK_fzmZ.js +0 -3
- package/.output/public/assets/preload-helper-sAZOZb3G.js +0 -5
- package/.output/public/assets/routes-DxF_s3cD.js +0 -1
- package/.output/server/_libs/@aws-crypto/crc32+[...].mjs +0 -419
- package/.output/server/_libs/@aws-sdk/checksums+[...].mjs +0 -10732
- package/.output/server/_libs/@aws-sdk/client-s3+[...].mjs +0 -9475
- package/.output/server/_libs/@aws-sdk/credential-provider-http+[...].mjs +0 -137
- package/.output/server/_libs/@aws-sdk/credential-provider-ini+[...].mjs +0 -440
- package/.output/server/_libs/@aws-sdk/credential-provider-process+[...].mjs +0 -62
- package/.output/server/_libs/@aws-sdk/credential-provider-sso+[...].mjs +0 -818
- package/.output/server/_libs/@aws-sdk/credential-provider-web-identity+[...].mjs +0 -59
- package/.output/server/_libs/@smithy/credential-provider-imds+[...].mjs +0 -342
- package/.output/server/_libs/aws-crypto__crc32c.mjs +0 -314
- package/.output/server/_libs/aws-sdk__nested-clients.mjs +0 -2671
- package/.output/server/_ssr/SignIn-sHQaBQJC.mjs +0 -53
- package/.output/server/_ssr/artifacts-Cd7Qx3Qp.mjs +0 -2
- package/.output/server/_ssr/auth-0AamrwHo.mjs +0 -38355
- package/.output/server/_ssr/auth-client-lSMb_Idt.mjs +0 -1070
- package/.output/server/_ssr/bun-sqlite-dialect-BW9W1_Ps-DLIXocA0.mjs +0 -162
- package/.output/server/_ssr/core-RrmWoKne.mjs +0 -5
- package/.output/server/_ssr/d1-sqlite-dialect-BLC8LXE6-BoFMIPr_.mjs +0 -112
- package/.output/server/_ssr/db-BzvMQ2kx.mjs +0 -2
- package/.output/server/_ssr/dist-ClzqkwxD.mjs +0 -342
- package/.output/server/_ssr/factory-ixUjZqJn.mjs +0 -6903
- package/.output/server/_ssr/kysely-adapter-DkZbmKgV.mjs +0 -2
- package/.output/server/_ssr/kysely-migration-tables-JkVUjPF_-bPAX9LR_.mjs +0 -19
- package/.output/server/_ssr/logger-DDjsYB2X.mjs +0 -2
- package/.output/server/_ssr/loopTags-CXx5UdGf.mjs +0 -3356
- package/.output/server/_ssr/node-sqlite-dialect-BbUqnnYv.mjs +0 -162
- package/.output/server/_ssr/router-BlLlhfJe.mjs +0 -543
- package/.output/server/_ssr/routes-DTBltabU.mjs +0 -27
- package/.output/server/_ssr/start-4ifArtKx.mjs +0 -4
- package/.output/server/_ssr/store-vDZ9INSa.mjs +0 -2
- package/.output/server/_ssr/tokens-BHiCElYD.mjs +0 -2
- package/.output/server/drizzle/0001_add_pi_agent.sql +0 -9
- package/.output/server/drizzle/0002_remove_teams.sql +0 -15
- package/.output/server/drizzle/0003_add_loop_tags.sql +0 -5
- package/.output/server/node_modules/tslib/modules/index.js +0 -70
- package/.output/server/node_modules/tslib/modules/package.json +0 -3
- package/.output/server/node_modules/tslib/package.json +0 -47
- package/.output/server/node_modules/tslib/tslib.js +0 -484
- package/.output/server/package.json +0 -9
- package/drizzle/0001_add_pi_agent.sql +0 -9
- package/drizzle/0002_remove_teams.sql +0 -15
- package/drizzle/0003_add_loop_tags.sql +0 -5
- package/scripts/prestart.mjs +0 -66
|
@@ -1,17 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { requestScope } from "./auth-0AamrwHo.mjs";
|
|
4
|
-
import { n as createServerFn } from "./ssr.mjs";
|
|
1
|
+
import { A as listMachines$1, C as getMachine, F as machineIdFromToken, L as mintDeviceToken, P as loopsForMachine, W as sha256, Y as updateMachine, b as forceDeleteMachine, c as ensureServer, n as MIN_DAEMON_VERSION, s as daemonNeedsUpdate, v as createMachine$1 } from "./boot-CYRi-wez.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-
|
|
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-CniGcY8s.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.
|
|
97
|
-
*
|
|
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
|
-
|
|
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:
|
|
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
|
|
136
|
-
|
|
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
|
|
172
|
-
return machine ? toSummary(machine
|
|
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
|
|
195
|
-
if (
|
|
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 };
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { c as ensureServer, d as readJsonBody, f as validOptionalPollString, m as validPollWireId, p as validPollCurrentRun, r as POLL_HTTP_BODY_FIELDS, t as MACHINE_BODY_CAP, u as readByteBody } from "./boot-CYRi-wez.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$10 } from "./artifactKind-DqpmtxDU.mjs";
|
|
5
|
+
import { t as Route$11 } from "./loops._loopId_.runs._runId-BFP9oMrp.mjs";
|
|
6
|
+
import { n as Route$12 } from "./routes-DnE7XDe_.mjs";
|
|
7
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/router-BFIH2UAE.js
|
|
8
|
+
var import_jsx_runtime = require_jsx_runtime();
|
|
9
|
+
var app_default = "/assets/app-DXLuQNb9.css";
|
|
10
|
+
var Route$9 = 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, MACHINE_BODY_CAP);
|
|
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$8 = 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-CYRi-wez.mjs").then((n) => n.i).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$7 = createFileRoute("/api/ready")({ server: { handlers: { GET: () => readyResponse() } } });
|
|
82
|
+
var Route$6 = createFileRoute("/api/health")({ server: { handlers: { GET: async () => Response.json({ ok: true }) } } });
|
|
83
|
+
/**
|
|
84
|
+
* POST /api/machine/sync — exact artifact sync (Bearer DEVICE token). The daemon
|
|
85
|
+
* posts the complete configured-path manifest after a run; the server reconciles
|
|
86
|
+
* artifact_files and replies with needHashes for raw blob PUTs.
|
|
87
|
+
* The manifest route retains its dedicated 32 MB JSON-body limit.
|
|
88
|
+
*
|
|
89
|
+
* This byte-ingress route is deliberately NOT rate limited (same rationale as the
|
|
90
|
+
* blob PUT): it requires a valid registered device token (unknown ⇒ 401) and is
|
|
91
|
+
* already bounded by the exact-path/hash handshake and 32MB sync-body cap, so a
|
|
92
|
+
* limiter would only throttle a legitimate large sync.
|
|
93
|
+
*/
|
|
94
|
+
var Route$5 = createFileRoute("/api/machine/sync")({ server: { handlers: { POST: async ({ request }) => {
|
|
95
|
+
const auth = request.headers.get("authorization") ?? "";
|
|
96
|
+
const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
|
|
97
|
+
if (!token) return Response.json({ error: "missing device token" }, { status: 401 });
|
|
98
|
+
const { SYNC_BODY_CAP, parseArtifactSyncBody } = await import("./boot-CYRi-wez.mjs").then((n) => n.N).then((n) => n.v);
|
|
99
|
+
const parsed = await readJsonBody(request, SYNC_BODY_CAP);
|
|
100
|
+
if (parsed.kind === "too-large") return Response.json({ error: "sync body too large" }, { status: 413 });
|
|
101
|
+
if (parsed.kind === "invalid") return Response.json({ error: "invalid JSON" }, { status: 400 });
|
|
102
|
+
const body = parseArtifactSyncBody(parsed.body);
|
|
103
|
+
if (!body) return Response.json({ error: "invalid sync body" }, { status: 400 });
|
|
104
|
+
const { getArtifactSync } = await import("./boot-CYRi-wez.mjs").then((n) => n.i).then((n) => n.t);
|
|
105
|
+
const r = await (await getArtifactSync()).sync(token, body);
|
|
106
|
+
return Response.json(r.body, { status: r.status });
|
|
107
|
+
} } } });
|
|
108
|
+
var Route$4 = createFileRoute("/api/machine/status")({ server: { handlers: { GET: async ({ request }) => {
|
|
109
|
+
const auth = request.headers.get("authorization") ?? "";
|
|
110
|
+
const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
|
|
111
|
+
if (!token) return Response.json({ error: "missing device token" }, { status: 401 });
|
|
112
|
+
const { getGateway } = await import("./boot-CYRi-wez.mjs").then((n) => n.i).then((n) => n.t);
|
|
113
|
+
const r = await (await getGateway()).status(token);
|
|
114
|
+
return Response.json(r.body, { status: r.status });
|
|
115
|
+
} } } });
|
|
116
|
+
/** Reject non-object bodies, additions, coercible types, over-cap strings, and
|
|
117
|
+
* NUL-bearing text before the gateway is loaded or any state can mutate. */
|
|
118
|
+
function parsePollBody(value) {
|
|
119
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
120
|
+
if (Object.keys(value).some((key) => !POLL_HTTP_BODY_FIELDS.has(key))) return null;
|
|
121
|
+
const body = value;
|
|
122
|
+
if (!validOptionalPollString(body.host, 524288) || !validOptionalPollString(body.platform, 524288) || !validOptionalPollString(body.arch, 524288) || !validOptionalPollString(body.version, 64)) return null;
|
|
123
|
+
if (body.protocolVersion !== void 0 && (typeof body.protocolVersion !== "number" || !Number.isInteger(body.protocolVersion))) return null;
|
|
124
|
+
if (body.currentRuns !== void 0 && (!Array.isArray(body.currentRuns) || body.currentRuns.some((run) => !validPollCurrentRun(run)))) return null;
|
|
125
|
+
if (body.daemonInstanceId !== void 0 && !validPollWireId(body.daemonInstanceId)) return null;
|
|
126
|
+
if (body.recoveryComplete !== void 0 && typeof body.recoveryComplete !== "boolean") return null;
|
|
127
|
+
return body;
|
|
128
|
+
}
|
|
129
|
+
function gatewayPollRequest(body) {
|
|
130
|
+
return {
|
|
131
|
+
protocolVersion: body.protocolVersion,
|
|
132
|
+
currentRuns: body.currentRuns,
|
|
133
|
+
daemonInstanceId: body.daemonInstanceId,
|
|
134
|
+
recoveryComplete: body.recoveryComplete,
|
|
135
|
+
info: {
|
|
136
|
+
host: body.host,
|
|
137
|
+
platform: body.platform,
|
|
138
|
+
arch: body.arch,
|
|
139
|
+
version: body.version
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
var Route$3 = createFileRoute("/api/machine/poll")({ server: { handlers: { POST: async ({ request }) => {
|
|
144
|
+
return machineJsonRoute(request, {
|
|
145
|
+
missingCredential: "missing device token",
|
|
146
|
+
invalidBodyError: "unknown or invalid poll body fields",
|
|
147
|
+
invalidJsonError: "invalid JSON body",
|
|
148
|
+
parseBody: parsePollBody,
|
|
149
|
+
handle: async (token, body) => {
|
|
150
|
+
const { getGateway } = await import("./boot-CYRi-wez.mjs").then((n) => n.i).then((n) => n.t);
|
|
151
|
+
const r = await (await getGateway()).pollV4Wait(token, gatewayPollRequest(body));
|
|
152
|
+
return Response.json(r.body, { status: r.status });
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
} } } });
|
|
156
|
+
function parseCliBody(value) {
|
|
157
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
158
|
+
const body = value;
|
|
159
|
+
if (Object.keys(body).length !== 1 || !Object.prototype.hasOwnProperty.call(body, "argv")) return null;
|
|
160
|
+
if (!Array.isArray(body.argv) || body.argv.some((entry) => typeof entry !== "string")) return null;
|
|
161
|
+
return { argv: body.argv };
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* POST /api/machine/cli — the ONE unified CLI dispatch (Bearer credential + `{argv}`).
|
|
165
|
+
* `CliGateway.cli()` branches by credential type: a `dk_` device token takes owner
|
|
166
|
+
* commands, while an `rk_` run credential can only report its run. Uses the standard
|
|
167
|
+
* 2MB machine JSON body cap.
|
|
168
|
+
*/
|
|
169
|
+
var Route$2 = createFileRoute("/api/machine/cli")({ server: { handlers: { POST: async ({ request }) => {
|
|
170
|
+
return machineJsonRoute(request, {
|
|
171
|
+
missingCredential: "missing credential",
|
|
172
|
+
invalidJsonError: "body must be exactly {argv:string[]}",
|
|
173
|
+
invalidBodyError: "body must be exactly {argv:string[]}",
|
|
174
|
+
parseBody: parseCliBody,
|
|
175
|
+
handle: async (token, body) => {
|
|
176
|
+
const { getCliGateway } = await import("./boot-CYRi-wez.mjs").then((n) => n.i).then((n) => n.t);
|
|
177
|
+
const r = await (await getCliGateway()).cli(token, body.argv);
|
|
178
|
+
return Response.json(r.body, { status: r.status });
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
} } } });
|
|
182
|
+
/**
|
|
183
|
+
* `decodeURIComponent` that returns null on malformed percent-encoding instead
|
|
184
|
+
* of throwing a URIError (which would escape a route handler as a 500). Shared
|
|
185
|
+
* by the routes that decode path segments; the policy is uniform — ANY
|
|
186
|
+
* malformed segment is the caller's clean 400, never silently kept raw.
|
|
187
|
+
*/
|
|
188
|
+
function safeDecode(s) {
|
|
189
|
+
try {
|
|
190
|
+
return decodeURIComponent(s);
|
|
191
|
+
} catch {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* PUT /api/machine/blob/:hash — upload one content-addressed blob's raw bytes
|
|
197
|
+
* (Bearer DEVICE token). The server recomputes sha256(body) and rejects a
|
|
198
|
+
* mismatch before storing in the byte store. The hash is read from the URL (the daemon PUTs
|
|
199
|
+
* exactly the hashes the sync handshake returned in needHashes).
|
|
200
|
+
*
|
|
201
|
+
* This byte-ingress route is deliberately NOT rate limited: it requires a valid
|
|
202
|
+
* registered device token (unknown ⇒ 401, not an unauthenticated surface) and is
|
|
203
|
+
* already bounded by the sync hash-handshake (the server only accepts hashes it
|
|
204
|
+
* asked THIS machine for) plus the per-file 10MB byte cap. A
|
|
205
|
+
* large first sync bursts many concurrent PUTs on one token, so any limiter would
|
|
206
|
+
* only throttle legitimate uploads without adding real protection.
|
|
207
|
+
*/
|
|
208
|
+
var Route$1 = createFileRoute("/api/machine/blob/$hash")({ server: { handlers: { PUT: async ({ request }) => {
|
|
209
|
+
const auth = request.headers.get("authorization") ?? "";
|
|
210
|
+
const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
|
|
211
|
+
if (!token) return Response.json({ error: "missing device token" }, { status: 401 });
|
|
212
|
+
const hash = safeDecode(new URL(request.url).pathname.split("/").pop() ?? "");
|
|
213
|
+
if (hash === null) return Response.json({ error: "bad hash" }, { status: 400 });
|
|
214
|
+
const { BLOB_CAP } = await import("./boot-CYRi-wez.mjs").then((n) => n.N).then((n) => n.v);
|
|
215
|
+
const body = await readByteBody(request, BLOB_CAP);
|
|
216
|
+
if (body.kind === "too-large") return Response.json({ error: "blob exceeds size cap" }, { status: 413 });
|
|
217
|
+
if (body.kind === "invalid") return Response.json({ error: "could not read blob body" }, { status: 400 });
|
|
218
|
+
const buf = Buffer.from(body.bytes.buffer, body.bytes.byteOffset, body.bytes.byteLength);
|
|
219
|
+
const { getArtifactSync } = await import("./boot-CYRi-wez.mjs").then((n) => n.i).then((n) => n.t);
|
|
220
|
+
const r = await (await getArtifactSync()).putBlob(token, hash, buf);
|
|
221
|
+
return Response.json(r.body, { status: r.status });
|
|
222
|
+
} } } });
|
|
223
|
+
/**
|
|
224
|
+
* GET /api/artifact/:loopId/:path - stream one of a loop's live-synced artifact
|
|
225
|
+
* blobs. 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. Path-safe: the relative path is
|
|
237
|
+
* normalized + traversal-rejected before it ever reaches a blob, and blob keys
|
|
238
|
+
* are content hashes, never the user path. Reads stream from the local
|
|
239
|
+
* BlobStore; the route never writes.
|
|
240
|
+
*/
|
|
241
|
+
var PREFIX = "/api/artifact/";
|
|
242
|
+
var Route = createFileRoute("/api/artifact/$loopId/$")({ server: { handlers: { GET: async ({ request }) => {
|
|
243
|
+
const pathname = new URL(request.url).pathname;
|
|
244
|
+
if (!pathname.startsWith(PREFIX)) return Response.json({ error: "not found" }, { status: 404 });
|
|
245
|
+
const after = pathname.slice(14);
|
|
246
|
+
const slash = after.indexOf("/");
|
|
247
|
+
if (slash < 0) return Response.json({ error: "missing path" }, { status: 400 });
|
|
248
|
+
const loopId = safeDecode(after.slice(0, slash));
|
|
249
|
+
if (loopId === null) return Response.json({ error: "bad loop id" }, { status: 400 });
|
|
250
|
+
const segments = after.slice(slash + 1).split("/").map((s) => safeDecode(s));
|
|
251
|
+
if (segments.some((s) => s === null)) return Response.json({ error: "bad path" }, { status: 400 });
|
|
252
|
+
const relPath = segments.join("/");
|
|
253
|
+
if (!await (await import("./boot-CYRi-wez.mjs").then((n) => n.N).then((n) => n.It)).getLoop(loopId)) return Response.json({ error: "not found" }, { status: 404 });
|
|
254
|
+
const { readLoopArtifactBytes } = await import("./artifactFiles-Di1xDKvX.mjs");
|
|
255
|
+
const r = await readLoopArtifactBytes(loopId, relPath);
|
|
256
|
+
if (r.status !== 200 || !r.bytes) return Response.json({ error: "not found" }, { status: r.status });
|
|
257
|
+
const filename = (r.filename || "file").replace(/["\\\r\n]/g, "_");
|
|
258
|
+
const mime = new URL(request.url).searchParams.get("view") === "inline" ? imageMime(relPath) : null;
|
|
259
|
+
if (mime) return new Response(new Uint8Array(r.bytes), {
|
|
260
|
+
status: 200,
|
|
261
|
+
headers: {
|
|
262
|
+
"content-type": mime,
|
|
263
|
+
"content-length": String(r.bytes.length),
|
|
264
|
+
"content-disposition": `inline; filename="${filename}"`,
|
|
265
|
+
"x-content-type-options": "nosniff",
|
|
266
|
+
"content-security-policy": "sandbox",
|
|
267
|
+
"cache-control": "private, no-store"
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
return new Response(new Uint8Array(r.bytes), {
|
|
271
|
+
status: 200,
|
|
272
|
+
headers: {
|
|
273
|
+
"content-type": r.binary ? "application/octet-stream" : "text/plain; charset=utf-8",
|
|
274
|
+
"content-length": String(r.bytes.length),
|
|
275
|
+
"content-disposition": `attachment; filename="${filename}"`,
|
|
276
|
+
"cache-control": "private, no-store"
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
} } } });
|
|
280
|
+
var IndexRoute = Route$12.update({
|
|
281
|
+
id: "/",
|
|
282
|
+
path: "/",
|
|
283
|
+
getParentRoute: () => Route$9
|
|
284
|
+
});
|
|
285
|
+
var MachineReportRoute = Route$8.update({
|
|
286
|
+
id: "/machine/report",
|
|
287
|
+
path: "/machine/report",
|
|
288
|
+
getParentRoute: () => Route$9
|
|
289
|
+
});
|
|
290
|
+
var LoopsLoopIdRoute = Route$10.update({
|
|
291
|
+
id: "/loops/$loopId",
|
|
292
|
+
path: "/loops/$loopId",
|
|
293
|
+
getParentRoute: () => Route$9
|
|
294
|
+
});
|
|
295
|
+
var ApiReadyRoute = Route$7.update({
|
|
296
|
+
id: "/api/ready",
|
|
297
|
+
path: "/api/ready",
|
|
298
|
+
getParentRoute: () => Route$9
|
|
299
|
+
});
|
|
300
|
+
var ApiHealthRoute = Route$6.update({
|
|
301
|
+
id: "/api/health",
|
|
302
|
+
path: "/api/health",
|
|
303
|
+
getParentRoute: () => Route$9
|
|
304
|
+
});
|
|
305
|
+
var ApiMachineSyncRoute = Route$5.update({
|
|
306
|
+
id: "/api/machine/sync",
|
|
307
|
+
path: "/api/machine/sync",
|
|
308
|
+
getParentRoute: () => Route$9
|
|
309
|
+
});
|
|
310
|
+
var ApiMachineStatusRoute = Route$4.update({
|
|
311
|
+
id: "/api/machine/status",
|
|
312
|
+
path: "/api/machine/status",
|
|
313
|
+
getParentRoute: () => Route$9
|
|
314
|
+
});
|
|
315
|
+
var ApiMachinePollRoute = Route$3.update({
|
|
316
|
+
id: "/api/machine/poll",
|
|
317
|
+
path: "/api/machine/poll",
|
|
318
|
+
getParentRoute: () => Route$9
|
|
319
|
+
});
|
|
320
|
+
var ApiMachineCliRoute = Route$2.update({
|
|
321
|
+
id: "/api/machine/cli",
|
|
322
|
+
path: "/api/machine/cli",
|
|
323
|
+
getParentRoute: () => Route$9
|
|
324
|
+
});
|
|
325
|
+
var LoopsLoopIdRunsRunIdRoute = Route$11.update({
|
|
326
|
+
id: "/loops/$loopId_/runs/$runId",
|
|
327
|
+
path: "/loops/$loopId/runs/$runId",
|
|
328
|
+
getParentRoute: () => Route$9
|
|
329
|
+
});
|
|
330
|
+
var ApiMachineBlobHashRoute = Route$1.update({
|
|
331
|
+
id: "/api/machine/blob/$hash",
|
|
332
|
+
path: "/api/machine/blob/$hash",
|
|
333
|
+
getParentRoute: () => Route$9
|
|
334
|
+
});
|
|
335
|
+
var rootRouteChildren = {
|
|
336
|
+
IndexRoute,
|
|
337
|
+
ApiHealthRoute,
|
|
338
|
+
ApiReadyRoute,
|
|
339
|
+
LoopsLoopIdRoute,
|
|
340
|
+
MachineReportRoute,
|
|
341
|
+
ApiMachineCliRoute,
|
|
342
|
+
ApiMachinePollRoute,
|
|
343
|
+
ApiMachineStatusRoute,
|
|
344
|
+
ApiMachineSyncRoute,
|
|
345
|
+
ApiArtifactLoopIdSplatRoute: Route.update({
|
|
346
|
+
id: "/api/artifact/$loopId/$",
|
|
347
|
+
path: "/api/artifact/$loopId/$",
|
|
348
|
+
getParentRoute: () => Route$9
|
|
349
|
+
}),
|
|
350
|
+
ApiMachineBlobHashRoute,
|
|
351
|
+
LoopsLoopIdRunsRunIdRoute
|
|
352
|
+
};
|
|
353
|
+
var routeTree = Route$9._addFileChildren(rootRouteChildren)._addFileTypes();
|
|
354
|
+
function getRouter() {
|
|
355
|
+
return createRouter({
|
|
356
|
+
routeTree,
|
|
357
|
+
scrollRestoration: true,
|
|
358
|
+
defaultPreload: "intent"
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
//#endregion
|
|
362
|
+
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-DnE7XDe_.mjs";
|
|
3
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/routes-BSVBANI3.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 };
|