@kky42/pievo-server 2.4.13 → 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-D8m1FDHO.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-79D939J4.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-DBIP648Z.mjs → MachinesModal-CmVMec6B.mjs} +52 -46
- package/.output/server/_ssr/{actionUi-C_WswNb1.mjs → actionUi-DE-Pb2tv.mjs} +4 -5
- package/.output/server/_ssr/{artifactFiles-BvVmixrs.mjs → artifactFiles-Di1xDKvX.mjs} +5 -7
- package/.output/server/_ssr/{artifactKind-DRCbYHZU.mjs → artifactKind-DqpmtxDU.mjs} +3 -9
- package/.output/server/_ssr/{boot-U-CFI65B.mjs → boot-CYRi-wez.mjs} +531 -1976
- package/.output/server/_ssr/lifecycleUi-BsbUsi5K.mjs +106 -0
- package/.output/server/_ssr/{loopApi-C4nN0msL.mjs → loopApi-Bez1rHV0.mjs} +50 -65
- package/.output/server/_ssr/{loopProjection-DMSNHux6.mjs → loopProjection-esCjyZJg.mjs} +4 -20
- package/.output/server/_ssr/{loops._loopId-Q4pRYPip.mjs → loops._loopId-KUezNY69.mjs} +133 -24
- package/.output/server/_ssr/{loops._loopId_.runs._runId-DNXVmCt5.mjs → loops._loopId_.runs._runId-BFP9oMrp.mjs} +2 -7
- package/.output/server/_ssr/{loops._loopId_.runs._runId-ty3AhkOi.mjs → loops._loopId_.runs._runId-DOkUEesG.mjs} +18 -33
- package/.output/server/_ssr/{machineFns-CkNms3zA.mjs → machineFns-CniGcY8s.mjs} +13 -49
- 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-BaanDO41.mjs → routes-DnE7XDe_.mjs} +242 -77
- package/.output/server/_ssr/{routes-B3fulW5I.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-DkmxsHDK.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 -16
- package/.output/server/index.mjs +43 -50
- package/drizzle/0000_baseline.sql +6 -97
- package/drizzle/meta/_journal.json +2 -16
- 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-CDCdDD1a.js +0 -1
- package/.output/public/assets/app-PPhgN9iN.css +0 -2
- package/.output/public/assets/index-c0XPWXuz.js +0 -10
- package/.output/public/assets/loops._loopId-D7i_BGiE.js +0 -67
- package/.output/public/assets/loops._loopId_.runs._runId-CksSrEY7.js +0 -3
- package/.output/public/assets/preload-helper-Zcmr31XG.js +0 -5
- package/.output/public/assets/routes-BYgl3Tmv.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-Bp3uWaCQ.mjs +0 -53
- package/.output/server/_ssr/artifacts-CzDJLaa6.mjs +0 -2
- package/.output/server/_ssr/auth-BV0MxVdo.mjs +0 -38355
- package/.output/server/_ssr/auth-client-RTpMpT4L.mjs +0 -1070
- package/.output/server/_ssr/boot-CsLXrLEa.mjs +0 -2
- package/.output/server/_ssr/bun-sqlite-dialect-BW9W1_Ps-D9KUBJ-L.mjs +0 -162
- package/.output/server/_ssr/core-RrmWoKne.mjs +0 -5
- package/.output/server/_ssr/d1-sqlite-dialect-BLC8LXE6-CVHRzH1n.mjs +0 -112
- package/.output/server/_ssr/db-BTX-xORB.mjs +0 -2
- package/.output/server/_ssr/dist-D7UDVZ5G.mjs +0 -342
- package/.output/server/_ssr/factory-DYFx1fg-.mjs +0 -6903
- package/.output/server/_ssr/kysely-adapter-CwdB7yBh.mjs +0 -2
- package/.output/server/_ssr/kysely-migration-tables-JkVUjPF_-bPAX9LR_.mjs +0 -19
- package/.output/server/_ssr/logger-CwiKSMp6.mjs +0 -2
- package/.output/server/_ssr/node-sqlite-dialect-B_g92oFK.mjs +0 -162
- package/.output/server/_ssr/router-DCNZ2xLm.mjs +0 -543
- package/.output/server/_ssr/routes-8JSF7bX_.mjs +0 -27
- package/.output/server/_ssr/runDiff-D7CxAgYL.mjs +0 -2
- package/.output/server/_ssr/start-4ifArtKx.mjs +0 -4
- package/.output/server/_ssr/store-DSNbXxqz.mjs +0 -2
- package/.output/server/_ssr/tokens-DzUv7CCp.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/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/scripts/prestart.mjs +0 -66
|
@@ -1,543 +0,0 @@
|
|
|
1
|
-
import { j as sql } from "../_libs/drizzle-orm+postgres.mjs";
|
|
2
|
-
import { Mt as validPollCurrentRun, Nt as validPollWireId, d as POLL_HTTP_BODY_FIELDS, jt as validOptionalPollString, l as MACHINE_BODY_CAP, mt as readJsonBody, pt as readByteBody, w as ensureServer } from "./boot-U-CFI65B.mjs";
|
|
3
|
-
import { h as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
|
|
4
|
-
import { c as createFileRoute, i as HeadContent, l as createRootRoute, o as createRouter, r as Scripts } from "../_libs/@tanstack/react-router+[...].mjs";
|
|
5
|
-
import { r as imageMime, t as Route$12 } from "./artifactKind-DRCbYHZU.mjs";
|
|
6
|
-
import { t as Route$13 } from "./loops._loopId_.runs._runId-DNXVmCt5.mjs";
|
|
7
|
-
import { n as Route$14 } from "./routes-BaanDO41.mjs";
|
|
8
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/router-DCNZ2xLm.js
|
|
9
|
-
var import_jsx_runtime = require_jsx_runtime();
|
|
10
|
-
var app_default = "/assets/app-PPhgN9iN.css";
|
|
11
|
-
var Route$11 = createRootRoute({
|
|
12
|
-
head: () => ({
|
|
13
|
-
meta: [
|
|
14
|
-
{ charSet: "utf-8" },
|
|
15
|
-
{
|
|
16
|
-
name: "viewport",
|
|
17
|
-
content: "width=device-width, initial-scale=1"
|
|
18
|
-
},
|
|
19
|
-
{ title: "Pievo" }
|
|
20
|
-
],
|
|
21
|
-
links: [{
|
|
22
|
-
rel: "stylesheet",
|
|
23
|
-
href: app_default
|
|
24
|
-
}, {
|
|
25
|
-
rel: "icon",
|
|
26
|
-
type: "image/svg+xml",
|
|
27
|
-
href: "/favicon.svg"
|
|
28
|
-
}]
|
|
29
|
-
}),
|
|
30
|
-
shellComponent: RootDocument
|
|
31
|
-
});
|
|
32
|
-
function RootDocument({ children }) {
|
|
33
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("html", {
|
|
34
|
-
lang: "en",
|
|
35
|
-
suppressHydrationWarning: true,
|
|
36
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("head", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HeadContent, {}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("body", {
|
|
37
|
-
className: "dot-grid-subtle bg-paper font-sans text-primary antialiased",
|
|
38
|
-
suppressHydrationWarning: true,
|
|
39
|
-
children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Scripts, {})]
|
|
40
|
-
})]
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
var TokenBucketLimiter = class {
|
|
44
|
-
capacity;
|
|
45
|
-
refillPerSec;
|
|
46
|
-
maxKeys;
|
|
47
|
-
buckets = /* @__PURE__ */ new Map();
|
|
48
|
-
constructor(capacity, refillPerSec, maxKeys = 2e4) {
|
|
49
|
-
this.capacity = capacity;
|
|
50
|
-
this.refillPerSec = refillPerSec;
|
|
51
|
-
this.maxKeys = maxKeys;
|
|
52
|
-
}
|
|
53
|
-
allow(key, now = Date.now()) {
|
|
54
|
-
let b = this.buckets.get(key);
|
|
55
|
-
if (!b) {
|
|
56
|
-
if (this.buckets.size >= this.maxKeys) this.evictOldest();
|
|
57
|
-
b = {
|
|
58
|
-
tokens: this.capacity,
|
|
59
|
-
last: now
|
|
60
|
-
};
|
|
61
|
-
this.buckets.set(key, b);
|
|
62
|
-
}
|
|
63
|
-
const elapsedSec = Math.max(0, (now - b.last) / 1e3);
|
|
64
|
-
b.tokens = Math.min(this.capacity, b.tokens + elapsedSec * this.refillPerSec);
|
|
65
|
-
b.last = now;
|
|
66
|
-
if (b.tokens >= 1) {
|
|
67
|
-
b.tokens -= 1;
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
/** Drop the least-recently-touched key (bounded memory under a distinct-key flood). */
|
|
73
|
-
evictOldest() {
|
|
74
|
-
let oldestKey;
|
|
75
|
-
let oldest = Number.POSITIVE_INFINITY;
|
|
76
|
-
for (const [k, b] of this.buckets) if (b.last < oldest) {
|
|
77
|
-
oldest = b.last;
|
|
78
|
-
oldestKey = k;
|
|
79
|
-
}
|
|
80
|
-
if (oldestKey !== void 0) this.buckets.delete(oldestKey);
|
|
81
|
-
}
|
|
82
|
-
reset() {
|
|
83
|
-
this.buckets.clear();
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
function envNum(name, dflt) {
|
|
87
|
-
const raw = process.env[name]?.trim();
|
|
88
|
-
if (!raw) return dflt;
|
|
89
|
-
const n = Number(raw);
|
|
90
|
-
return Number.isFinite(n) && n > 0 ? n : dflt;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Per-IP: default 240 burst + 8/sec sustained (≈480 req/min). One daemon does
|
|
94
|
-
* ~20 polls/min plus sync bursts; this comfortably covers a dozen daemons behind a
|
|
95
|
-
* shared NAT while still capping a single-IP flood.
|
|
96
|
-
*/
|
|
97
|
-
var ipLimiter = new TokenBucketLimiter(envNum("PIEVO_RL_IP_BURST", 240), envNum("PIEVO_RL_IP_PER_SEC", 8));
|
|
98
|
-
/**
|
|
99
|
-
* Per-token (per-machine): default 120 burst + 4/sec sustained. A single daemon
|
|
100
|
-
* never approaches this; it bounds one credential's share of the process.
|
|
101
|
-
*/
|
|
102
|
-
var tokenLimiter = new TokenBucketLimiter(envNum("PIEVO_RL_TOKEN_BURST", 120), envNum("PIEVO_RL_TOKEN_PER_SEC", 4));
|
|
103
|
-
/** Whether rate limiting is active. Off by default in tests unless opted in, so the
|
|
104
|
-
* large existing suites don't trip it; ON in a real server process. */
|
|
105
|
-
function rateLimitEnabled() {
|
|
106
|
-
const flag = process.env.PIEVO_RATE_LIMIT?.trim().toLowerCase();
|
|
107
|
-
if (flag === "0" || flag === "false" || flag === "off") return false;
|
|
108
|
-
if (flag === "1" || flag === "true" || flag === "on") return true;
|
|
109
|
-
return process.env.VITEST !== "true";
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Best-effort client IP from the request, honoring the usual proxy headers (Fly
|
|
113
|
-
* sets `Fly-Client-IP`; a generic proxy sets `X-Forwarded-For`). Falls back to a
|
|
114
|
-
* single shared key so a missing IP still shares ONE bucket (fail-closed: unknown
|
|
115
|
-
* origins can't each get a private allowance). Only the FIRST `X-Forwarded-For` hop
|
|
116
|
-
* is trusted (the client-facing edge appends left-to-right).
|
|
117
|
-
*/
|
|
118
|
-
function clientIp(request) {
|
|
119
|
-
const h = request.headers;
|
|
120
|
-
const fly = h.get("fly-client-ip");
|
|
121
|
-
if (fly) return fly.trim();
|
|
122
|
-
const xff = h.get("x-forwarded-for");
|
|
123
|
-
if (xff) {
|
|
124
|
-
const first = xff.split(",")[0]?.trim();
|
|
125
|
-
if (first) return first;
|
|
126
|
-
}
|
|
127
|
-
const real = h.get("x-real-ip");
|
|
128
|
-
if (real) return real.trim();
|
|
129
|
-
return "unknown";
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* A 429 response with a Retry-After hint. Carries `text` + `exitCode` alongside
|
|
133
|
-
* `error` so `/api/machine/cli` can print the response directly.
|
|
134
|
-
*/
|
|
135
|
-
function tooMany() {
|
|
136
|
-
return Response.json({
|
|
137
|
-
error: "rate limited — slow down",
|
|
138
|
-
text: "rate limited — slow down",
|
|
139
|
-
exitCode: 1
|
|
140
|
-
}, {
|
|
141
|
-
status: 429,
|
|
142
|
-
headers: { "retry-after": "1" }
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Enforce the machine-route rate limits for one request. Call it at the top of a
|
|
147
|
-
* machine route handler, right after reading the Bearer token (pass it so a valid
|
|
148
|
-
* daemon keys off its own per-machine bucket). Returns a 429 `Response` to return
|
|
149
|
-
* verbatim, or null to proceed. Both tiers apply: the per-IP flood guard always,
|
|
150
|
-
* and the per-token fairness tier whenever a credential is present. (The byte-
|
|
151
|
-
* ingress routes are exempt entirely and simply do not call this — see the module
|
|
152
|
-
* doc.)
|
|
153
|
-
*/
|
|
154
|
-
function machineRouteLimit(request, token, opts = {}) {
|
|
155
|
-
if (!rateLimitEnabled()) return null;
|
|
156
|
-
const now = opts.now ?? Date.now();
|
|
157
|
-
if (!ipLimiter.allow(clientIp(request), now)) return tooMany();
|
|
158
|
-
if (token && !tokenLimiter.allow(token, now)) return tooMany();
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
var Route$10 = createFileRoute("/machine/report")({ server: { handlers: { POST: async ({ request }) => {
|
|
162
|
-
const auth = request.headers.get("authorization") ?? "";
|
|
163
|
-
const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
|
|
164
|
-
const limited = machineRouteLimit(request, token || void 0);
|
|
165
|
-
if (limited) return limited;
|
|
166
|
-
if (!token) return Response.json({ error: "missing token" }, { status: 401 });
|
|
167
|
-
const parsed = await readJsonBody(request, MACHINE_BODY_CAP);
|
|
168
|
-
if (parsed.kind === "too-large") return Response.json({ error: "body too large" }, { status: 413 });
|
|
169
|
-
const body = parsed.kind === "ok" && parsed.body && typeof parsed.body === "object" ? parsed.body : {};
|
|
170
|
-
const { getGateway } = await import("./boot-CsLXrLEa.mjs");
|
|
171
|
-
const r = await (await getGateway()).report(token, body);
|
|
172
|
-
return Response.json(r.body, { status: r.status });
|
|
173
|
-
} } } });
|
|
174
|
-
/**
|
|
175
|
-
* Deep readiness for process launchers. Unlike /api/health, this waits for the
|
|
176
|
-
* one-time backend boot (migrations, blob-store selection, and scheduler start).
|
|
177
|
-
* The per-launch nonce lets a launcher reject a different process already bound
|
|
178
|
-
* to the requested port.
|
|
179
|
-
*/
|
|
180
|
-
async function readyResponse(ensure = ensureServer, env = process.env) {
|
|
181
|
-
await ensure();
|
|
182
|
-
return Response.json({
|
|
183
|
-
ok: true,
|
|
184
|
-
nonce: env.PIEVO_SERVER_LAUNCH_NONCE ?? null
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
var Route$9 = createFileRoute("/api/ready")({ server: { handlers: { GET: () => readyResponse() } } });
|
|
188
|
-
var buildSha = () => process.env.GIT_SHA || "unknown";
|
|
189
|
-
var buildAt = () => process.env.BUILT_AT || "unknown";
|
|
190
|
-
var Route$8 = createFileRoute("/api/health")({ server: { handlers: { GET: async () => Response.json({
|
|
191
|
-
ok: true,
|
|
192
|
-
sha: buildSha(),
|
|
193
|
-
builtAt: buildAt()
|
|
194
|
-
}) } } });
|
|
195
|
-
/**
|
|
196
|
-
* POST /api/machine/sync — exact artifact sync (Bearer DEVICE token). The daemon
|
|
197
|
-
* posts the complete configured-path manifest after a run; the server reconciles
|
|
198
|
-
* artifact_files and replies with needHashes for raw blob PUTs.
|
|
199
|
-
* The manifest route retains its dedicated 32 MB JSON-body limit.
|
|
200
|
-
*
|
|
201
|
-
* This byte-ingress route is deliberately NOT rate limited (same rationale as the
|
|
202
|
-
* blob PUT): it requires a valid registered device token (unknown ⇒ 401) and is
|
|
203
|
-
* already bounded by the exact-path/hash handshake and 32MB sync-body cap, so a
|
|
204
|
-
* limiter would only throttle a legitimate large sync.
|
|
205
|
-
*/
|
|
206
|
-
var Route$7 = createFileRoute("/api/machine/sync")({ server: { handlers: { POST: async ({ request }) => {
|
|
207
|
-
const auth = request.headers.get("authorization") ?? "";
|
|
208
|
-
const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
|
|
209
|
-
if (!token) return Response.json({ error: "missing device token" }, { status: 401 });
|
|
210
|
-
const { SYNC_BODY_CAP, parseArtifactSyncBody } = await import("./artifacts-CzDJLaa6.mjs");
|
|
211
|
-
const parsed = await readJsonBody(request, SYNC_BODY_CAP);
|
|
212
|
-
if (parsed.kind === "too-large") return Response.json({ error: "sync body too large" }, { status: 413 });
|
|
213
|
-
if (parsed.kind === "invalid") return Response.json({ error: "invalid JSON" }, { status: 400 });
|
|
214
|
-
const body = parseArtifactSyncBody(parsed.body);
|
|
215
|
-
if (!body) return Response.json({ error: "invalid sync body" }, { status: 400 });
|
|
216
|
-
const { getArtifactSync } = await import("./boot-CsLXrLEa.mjs");
|
|
217
|
-
const r = await (await getArtifactSync()).sync(token, body);
|
|
218
|
-
return Response.json(r.body, { status: r.status });
|
|
219
|
-
} } } });
|
|
220
|
-
var Route$6 = createFileRoute("/api/machine/status")({ server: { handlers: { GET: async ({ request }) => {
|
|
221
|
-
const auth = request.headers.get("authorization") ?? "";
|
|
222
|
-
const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
|
|
223
|
-
const limited = machineRouteLimit(request, token || void 0);
|
|
224
|
-
if (limited) return limited;
|
|
225
|
-
if (!token) return Response.json({ error: "missing device token" }, { status: 401 });
|
|
226
|
-
const { getGateway } = await import("./boot-CsLXrLEa.mjs");
|
|
227
|
-
const r = await (await getGateway()).status(token);
|
|
228
|
-
return Response.json(r.body, { status: r.status });
|
|
229
|
-
} } } });
|
|
230
|
-
/** Reject non-object bodies, additions, coercible types, over-cap strings, and
|
|
231
|
-
* NUL-bearing text before the gateway is loaded or any state can mutate. */
|
|
232
|
-
function parsePollBody(value) {
|
|
233
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
234
|
-
if (Object.keys(value).some((key) => !POLL_HTTP_BODY_FIELDS.has(key))) return null;
|
|
235
|
-
const body = value;
|
|
236
|
-
if (!validOptionalPollString(body.host, 524288) || !validOptionalPollString(body.platform, 524288) || !validOptionalPollString(body.arch, 524288) || !validOptionalPollString(body.version, 64)) return null;
|
|
237
|
-
if (body.protocolVersion !== void 0 && (typeof body.protocolVersion !== "number" || !Number.isInteger(body.protocolVersion))) return null;
|
|
238
|
-
if (body.currentRuns !== void 0 && (!Array.isArray(body.currentRuns) || body.currentRuns.some((run) => !validPollCurrentRun(run)))) return null;
|
|
239
|
-
if (body.daemonInstanceId !== void 0 && !validPollWireId(body.daemonInstanceId)) return null;
|
|
240
|
-
if (body.recoveryComplete !== void 0 && typeof body.recoveryComplete !== "boolean") return null;
|
|
241
|
-
return body;
|
|
242
|
-
}
|
|
243
|
-
function gatewayPollRequest(body) {
|
|
244
|
-
return {
|
|
245
|
-
protocolVersion: body.protocolVersion,
|
|
246
|
-
currentRuns: body.currentRuns,
|
|
247
|
-
daemonInstanceId: body.daemonInstanceId,
|
|
248
|
-
recoveryComplete: body.recoveryComplete,
|
|
249
|
-
info: {
|
|
250
|
-
host: body.host,
|
|
251
|
-
platform: body.platform,
|
|
252
|
-
arch: body.arch,
|
|
253
|
-
version: body.version
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
var Route$5 = createFileRoute("/api/machine/poll")({ server: { handlers: { POST: async ({ request }) => {
|
|
258
|
-
const auth = request.headers.get("authorization") ?? "";
|
|
259
|
-
const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
|
|
260
|
-
const limited = machineRouteLimit(request, token || void 0);
|
|
261
|
-
if (limited) return limited;
|
|
262
|
-
if (!token) return Response.json({ error: "missing device token" }, { status: 401 });
|
|
263
|
-
const parsed = await readJsonBody(request, MACHINE_BODY_CAP);
|
|
264
|
-
if (parsed.kind === "too-large") return Response.json({ error: "body too large" }, { status: 413 });
|
|
265
|
-
if (parsed.kind !== "ok") return Response.json({ error: "invalid JSON body" }, { status: 400 });
|
|
266
|
-
const body = parsePollBody(parsed.body);
|
|
267
|
-
if (!body) return Response.json({ error: "unknown or invalid poll body fields" }, { status: 400 });
|
|
268
|
-
const { getGateway } = await import("./boot-CsLXrLEa.mjs");
|
|
269
|
-
const r = await (await getGateway()).pollV4Wait(token, gatewayPollRequest(body));
|
|
270
|
-
return Response.json(r.body, { status: r.status });
|
|
271
|
-
} } } });
|
|
272
|
-
function parseCliBody(value) {
|
|
273
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
274
|
-
const body = value;
|
|
275
|
-
if (Object.keys(body).length !== 1 || !Object.prototype.hasOwnProperty.call(body, "argv")) return null;
|
|
276
|
-
if (!Array.isArray(body.argv) || body.argv.some((entry) => typeof entry !== "string")) return null;
|
|
277
|
-
return { argv: body.argv };
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* POST /api/machine/cli — the ONE unified CLI dispatch (Bearer credential + `{argv}`).
|
|
281
|
-
* `CliGateway.cli()` branches by credential type: a `dk_` device token takes owner
|
|
282
|
-
* commands, while an `rk_` run credential can only report its run. Uses the standard
|
|
283
|
-
* 2MB machine JSON body cap.
|
|
284
|
-
*/
|
|
285
|
-
var Route$4 = createFileRoute("/api/machine/cli")({ server: { handlers: { POST: async ({ request }) => {
|
|
286
|
-
const auth = request.headers.get("authorization") ?? "";
|
|
287
|
-
const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
|
|
288
|
-
const limited = machineRouteLimit(request, token || void 0);
|
|
289
|
-
if (limited) return limited;
|
|
290
|
-
if (!token) return Response.json({ error: "missing credential" }, { status: 401 });
|
|
291
|
-
const parsed = await readJsonBody(request, MACHINE_BODY_CAP);
|
|
292
|
-
if (parsed.kind === "too-large") return Response.json({ error: "body too large" }, { status: 413 });
|
|
293
|
-
const body = parsed.kind === "ok" ? parseCliBody(parsed.body) : null;
|
|
294
|
-
if (!body) return Response.json({ error: "body must be exactly {argv:string[]}" }, { status: 400 });
|
|
295
|
-
const { getCliGateway } = await import("./boot-CsLXrLEa.mjs");
|
|
296
|
-
const r = await (await getCliGateway()).cli(token, body.argv);
|
|
297
|
-
return Response.json(r.body, { status: r.status });
|
|
298
|
-
} } } });
|
|
299
|
-
/**
|
|
300
|
-
* DB-touching liveness probe — the ONE home for the wedged-pool incident rationale
|
|
301
|
-
* (db/index.ts and the Fly checks point here). Unlike /api/health (which never
|
|
302
|
-
* touches the DB and so reported `ok` right through a ~5.5h outage while a dead
|
|
303
|
-
* postgres-js pool masked it), this runs a trivial `select 1` through the runtime
|
|
304
|
-
* pool.
|
|
305
|
-
*
|
|
306
|
-
* This route SURFACES a wedged pool (Fly's check goes critical, which de-routes the
|
|
307
|
-
* machine); it does NOT by itself recover it. Fly's failing `[http_service.checks]`
|
|
308
|
-
* only pull the machine from load balancing — they never restart the VM (the
|
|
309
|
-
* 2026-07-12 outage: pool wedged, check critical, box down ~9h with no auto-restart
|
|
310
|
-
* until a manual `fly machine restart`). The ACTUAL auto-recovery is the in-process
|
|
311
|
-
* DB watchdog (`server/dbWatchdog.ts`), which exits the process on a sustained wedge
|
|
312
|
-
* so Fly's `restart.policy = "on-failure"` brings up a fresh pool.
|
|
313
|
-
*
|
|
314
|
-
* A wedged pool makes `select 1` HANG (queued behind stuck connections; the pool's
|
|
315
|
-
* `statement_timeout` is unenforced under the Supabase transaction pooler). So the
|
|
316
|
-
* probe races the query against a hard client-side deadline (`PROBE_TIMEOUT_MS`, <
|
|
317
|
-
* Fly's 10s check timeout) and returns a fast 503 instead of eating the full timeout
|
|
318
|
-
* "awaiting headers". 200 → DB reachable; 503 → the query failed or timed out.
|
|
319
|
-
* Drizzle imported inside the handler so it stays out of the client bundle.
|
|
320
|
-
*/
|
|
321
|
-
var PROBE_TIMEOUT_MS = 5e3;
|
|
322
|
-
var Route$3 = createFileRoute("/api/health/db")({ server: { handlers: { GET: async () => {
|
|
323
|
-
try {
|
|
324
|
-
const { db } = await import("./db-BTX-xORB.mjs");
|
|
325
|
-
const ping = db.execute(sql`select 1`);
|
|
326
|
-
const timeout = new Promise((_, reject) => {
|
|
327
|
-
setTimeout(() => reject(/* @__PURE__ */ new Error(`db ping timed out after ${PROBE_TIMEOUT_MS}ms`)), PROBE_TIMEOUT_MS).unref?.();
|
|
328
|
-
});
|
|
329
|
-
await Promise.race([ping, timeout]);
|
|
330
|
-
return Response.json({
|
|
331
|
-
ok: true,
|
|
332
|
-
db: "up"
|
|
333
|
-
});
|
|
334
|
-
} catch (err) {
|
|
335
|
-
return Response.json({
|
|
336
|
-
ok: false,
|
|
337
|
-
db: "down",
|
|
338
|
-
error: err instanceof Error ? err.message : String(err)
|
|
339
|
-
}, { status: 503 });
|
|
340
|
-
}
|
|
341
|
-
} } } });
|
|
342
|
-
var Route$2 = createFileRoute("/api/auth/$")({ server: { handlers: {
|
|
343
|
-
GET: async ({ request }) => (await import("./auth-BV0MxVdo.mjs")).auth.handler(request),
|
|
344
|
-
POST: async ({ request }) => (await import("./auth-BV0MxVdo.mjs")).auth.handler(request)
|
|
345
|
-
} } });
|
|
346
|
-
/**
|
|
347
|
-
* `decodeURIComponent` that returns null on malformed percent-encoding instead
|
|
348
|
-
* of throwing a URIError (which would escape a route handler as a 500). Shared
|
|
349
|
-
* by the routes that decode path segments; the policy is uniform — ANY
|
|
350
|
-
* malformed segment is the caller's clean 400, never silently kept raw.
|
|
351
|
-
*/
|
|
352
|
-
function safeDecode(s) {
|
|
353
|
-
try {
|
|
354
|
-
return decodeURIComponent(s);
|
|
355
|
-
} catch {
|
|
356
|
-
return null;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* PUT /api/machine/blob/:hash — upload one content-addressed blob's raw bytes
|
|
361
|
-
* (Bearer DEVICE token). The server recomputes sha256(body) and rejects a
|
|
362
|
-
* mismatch before storing in the byte store. The hash is read from the URL (the daemon PUTs
|
|
363
|
-
* exactly the hashes the sync handshake returned in needHashes).
|
|
364
|
-
*
|
|
365
|
-
* This byte-ingress route is deliberately NOT rate limited: it requires a valid
|
|
366
|
-
* registered device token (unknown ⇒ 401, not an unauthenticated surface) and is
|
|
367
|
-
* already bounded by the sync hash-handshake (the server only accepts hashes it
|
|
368
|
-
* asked THIS machine for) plus the per-file 10MB byte cap. A
|
|
369
|
-
* large first sync bursts many concurrent PUTs on one token, so any limiter would
|
|
370
|
-
* only throttle legitimate uploads without adding real protection.
|
|
371
|
-
*/
|
|
372
|
-
var Route$1 = createFileRoute("/api/machine/blob/$hash")({ server: { handlers: { PUT: async ({ request }) => {
|
|
373
|
-
const auth = request.headers.get("authorization") ?? "";
|
|
374
|
-
const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
|
|
375
|
-
if (!token) return Response.json({ error: "missing device token" }, { status: 401 });
|
|
376
|
-
const hash = safeDecode(new URL(request.url).pathname.split("/").pop() ?? "");
|
|
377
|
-
if (hash === null) return Response.json({ error: "bad hash" }, { status: 400 });
|
|
378
|
-
const { BLOB_CAP } = await import("./artifacts-CzDJLaa6.mjs");
|
|
379
|
-
const body = await readByteBody(request, BLOB_CAP);
|
|
380
|
-
if (body.kind === "too-large") return Response.json({ error: "blob exceeds size cap" }, { status: 413 });
|
|
381
|
-
if (body.kind === "invalid") return Response.json({ error: "could not read blob body" }, { status: 400 });
|
|
382
|
-
const buf = Buffer.from(body.bytes.buffer, body.bytes.byteOffset, body.bytes.byteLength);
|
|
383
|
-
const { getArtifactSync } = await import("./boot-CsLXrLEa.mjs");
|
|
384
|
-
const r = await (await getArtifactSync()).putBlob(token, hash, buf);
|
|
385
|
-
return Response.json(r.body, { status: r.status });
|
|
386
|
-
} } } });
|
|
387
|
-
/**
|
|
388
|
-
* GET /api/artifact/:loopId/:path - stream one of a loop's live-synced artifact
|
|
389
|
-
* blobs. Two dispositions:
|
|
390
|
-
* - default: `attachment` (the download affordance for binary/oversize files;
|
|
391
|
-
* text is shown inline via getArtifact).
|
|
392
|
-
* - `?view=inline`: for a KNOWN image type only (the `imageMime` allowlist),
|
|
393
|
-
* served `inline` with the real image content-type so an <img> can render it.
|
|
394
|
-
* Hardened against a direct navigation turning the byte stream into a scripted
|
|
395
|
-
* document on the app origin: `X-Content-Type-Options: nosniff` pins the type
|
|
396
|
-
* and `Content-Security-Policy: sandbox` loads it in an opaque origin with no
|
|
397
|
-
* scripts - so an inline SVG can't run script against the session even if the
|
|
398
|
-
* URL is opened directly. Non-image inline requests fall back to attachment.
|
|
399
|
-
*
|
|
400
|
-
* Authed via the web session (not a machine token) and scoped to the loop owner,
|
|
401
|
-
* mirroring the server functions' `ownedLoop` gate. Path-safe: the relative path is
|
|
402
|
-
* normalized + traversal-rejected before it ever reaches a blob, and blob keys
|
|
403
|
-
* are content hashes, never the user path. Reads stream from the BlobStore
|
|
404
|
-
* (local by default, R2 when configured); the route never writes.
|
|
405
|
-
*/
|
|
406
|
-
var PREFIX = "/api/artifact/";
|
|
407
|
-
var Route = createFileRoute("/api/artifact/$loopId/$")({ server: { handlers: { GET: async ({ request }) => {
|
|
408
|
-
const pathname = new URL(request.url).pathname;
|
|
409
|
-
if (!pathname.startsWith(PREFIX)) return Response.json({ error: "not found" }, { status: 404 });
|
|
410
|
-
const after = pathname.slice(14);
|
|
411
|
-
const slash = after.indexOf("/");
|
|
412
|
-
if (slash < 0) return Response.json({ error: "missing path" }, { status: 400 });
|
|
413
|
-
const loopId = safeDecode(after.slice(0, slash));
|
|
414
|
-
if (loopId === null) return Response.json({ error: "bad loop id" }, { status: 400 });
|
|
415
|
-
const segments = after.slice(slash + 1).split("/").map((s) => safeDecode(s));
|
|
416
|
-
if (segments.some((s) => s === null)) return Response.json({ error: "bad path" }, { status: 400 });
|
|
417
|
-
const relPath = segments.join("/");
|
|
418
|
-
const loop = await (await import("./store-DSNbXxqz.mjs")).getLoop(loopId);
|
|
419
|
-
if (!loop) return Response.json({ error: "not found" }, { status: 404 });
|
|
420
|
-
const { requestScope, canAccessLoop } = await import("./auth-BV0MxVdo.mjs");
|
|
421
|
-
if (!canAccessLoop(loop.userId, await requestScope())) return Response.json({ error: "not found" }, { status: 404 });
|
|
422
|
-
const { readLoopArtifactBytes } = await import("./artifactFiles-BvVmixrs.mjs");
|
|
423
|
-
const r = await readLoopArtifactBytes(loopId, relPath);
|
|
424
|
-
if (r.status !== 200 || !r.bytes) return Response.json({ error: "not found" }, { status: r.status });
|
|
425
|
-
const filename = (r.filename || "file").replace(/["\\\r\n]/g, "_");
|
|
426
|
-
const mime = new URL(request.url).searchParams.get("view") === "inline" ? imageMime(relPath) : null;
|
|
427
|
-
if (mime) return new Response(new Uint8Array(r.bytes), {
|
|
428
|
-
status: 200,
|
|
429
|
-
headers: {
|
|
430
|
-
"content-type": mime,
|
|
431
|
-
"content-length": String(r.bytes.length),
|
|
432
|
-
"content-disposition": `inline; filename="${filename}"`,
|
|
433
|
-
"x-content-type-options": "nosniff",
|
|
434
|
-
"content-security-policy": "sandbox",
|
|
435
|
-
"cache-control": "private, no-store"
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
return new Response(new Uint8Array(r.bytes), {
|
|
439
|
-
status: 200,
|
|
440
|
-
headers: {
|
|
441
|
-
"content-type": r.binary ? "application/octet-stream" : "text/plain; charset=utf-8",
|
|
442
|
-
"content-length": String(r.bytes.length),
|
|
443
|
-
"content-disposition": `attachment; filename="${filename}"`,
|
|
444
|
-
"cache-control": "private, no-store"
|
|
445
|
-
}
|
|
446
|
-
});
|
|
447
|
-
} } } });
|
|
448
|
-
var IndexRoute = Route$14.update({
|
|
449
|
-
id: "/",
|
|
450
|
-
path: "/",
|
|
451
|
-
getParentRoute: () => Route$11
|
|
452
|
-
});
|
|
453
|
-
var MachineReportRoute = Route$10.update({
|
|
454
|
-
id: "/machine/report",
|
|
455
|
-
path: "/machine/report",
|
|
456
|
-
getParentRoute: () => Route$11
|
|
457
|
-
});
|
|
458
|
-
var LoopsLoopIdRoute = Route$12.update({
|
|
459
|
-
id: "/loops/$loopId",
|
|
460
|
-
path: "/loops/$loopId",
|
|
461
|
-
getParentRoute: () => Route$11
|
|
462
|
-
});
|
|
463
|
-
var ApiReadyRoute = Route$9.update({
|
|
464
|
-
id: "/api/ready",
|
|
465
|
-
path: "/api/ready",
|
|
466
|
-
getParentRoute: () => Route$11
|
|
467
|
-
});
|
|
468
|
-
var ApiHealthRoute = Route$8.update({
|
|
469
|
-
id: "/api/health",
|
|
470
|
-
path: "/api/health",
|
|
471
|
-
getParentRoute: () => Route$11
|
|
472
|
-
});
|
|
473
|
-
var ApiMachineSyncRoute = Route$7.update({
|
|
474
|
-
id: "/api/machine/sync",
|
|
475
|
-
path: "/api/machine/sync",
|
|
476
|
-
getParentRoute: () => Route$11
|
|
477
|
-
});
|
|
478
|
-
var ApiMachineStatusRoute = Route$6.update({
|
|
479
|
-
id: "/api/machine/status",
|
|
480
|
-
path: "/api/machine/status",
|
|
481
|
-
getParentRoute: () => Route$11
|
|
482
|
-
});
|
|
483
|
-
var ApiMachinePollRoute = Route$5.update({
|
|
484
|
-
id: "/api/machine/poll",
|
|
485
|
-
path: "/api/machine/poll",
|
|
486
|
-
getParentRoute: () => Route$11
|
|
487
|
-
});
|
|
488
|
-
var ApiMachineCliRoute = Route$4.update({
|
|
489
|
-
id: "/api/machine/cli",
|
|
490
|
-
path: "/api/machine/cli",
|
|
491
|
-
getParentRoute: () => Route$11
|
|
492
|
-
});
|
|
493
|
-
var ApiHealthDbRoute = Route$3.update({
|
|
494
|
-
id: "/db",
|
|
495
|
-
path: "/db",
|
|
496
|
-
getParentRoute: () => ApiHealthRoute
|
|
497
|
-
});
|
|
498
|
-
var ApiAuthSplatRoute = Route$2.update({
|
|
499
|
-
id: "/api/auth/$",
|
|
500
|
-
path: "/api/auth/$",
|
|
501
|
-
getParentRoute: () => Route$11
|
|
502
|
-
});
|
|
503
|
-
var LoopsLoopIdRunsRunIdRoute = Route$13.update({
|
|
504
|
-
id: "/loops/$loopId_/runs/$runId",
|
|
505
|
-
path: "/loops/$loopId/runs/$runId",
|
|
506
|
-
getParentRoute: () => Route$11
|
|
507
|
-
});
|
|
508
|
-
var ApiMachineBlobHashRoute = Route$1.update({
|
|
509
|
-
id: "/api/machine/blob/$hash",
|
|
510
|
-
path: "/api/machine/blob/$hash",
|
|
511
|
-
getParentRoute: () => Route$11
|
|
512
|
-
});
|
|
513
|
-
var ApiArtifactLoopIdSplatRoute = Route.update({
|
|
514
|
-
id: "/api/artifact/$loopId/$",
|
|
515
|
-
path: "/api/artifact/$loopId/$",
|
|
516
|
-
getParentRoute: () => Route$11
|
|
517
|
-
});
|
|
518
|
-
var ApiHealthRouteChildren = { ApiHealthDbRoute };
|
|
519
|
-
var rootRouteChildren = {
|
|
520
|
-
IndexRoute,
|
|
521
|
-
ApiHealthRoute: ApiHealthRoute._addFileChildren(ApiHealthRouteChildren),
|
|
522
|
-
ApiReadyRoute,
|
|
523
|
-
LoopsLoopIdRoute,
|
|
524
|
-
MachineReportRoute,
|
|
525
|
-
ApiAuthSplatRoute,
|
|
526
|
-
ApiMachineCliRoute,
|
|
527
|
-
ApiMachinePollRoute,
|
|
528
|
-
ApiMachineStatusRoute,
|
|
529
|
-
ApiMachineSyncRoute,
|
|
530
|
-
ApiArtifactLoopIdSplatRoute,
|
|
531
|
-
ApiMachineBlobHashRoute,
|
|
532
|
-
LoopsLoopIdRunsRunIdRoute
|
|
533
|
-
};
|
|
534
|
-
var routeTree = Route$11._addFileChildren(rootRouteChildren)._addFileTypes();
|
|
535
|
-
function getRouter() {
|
|
536
|
-
return createRouter({
|
|
537
|
-
routeTree,
|
|
538
|
-
scrollRestoration: true,
|
|
539
|
-
defaultPreload: "intent"
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
//#endregion
|
|
543
|
-
export { getRouter };
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { h as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
|
|
2
|
-
import { O as useSession } from "./auth-client-RTpMpT4L.mjs";
|
|
3
|
-
import { a as Loading } from "./actionUi-C_WswNb1.mjs";
|
|
4
|
-
import { t as SignIn } from "./SignIn-Bp3uWaCQ.mjs";
|
|
5
|
-
import { n as Route, t as DashboardView } from "./routes-BaanDO41.mjs";
|
|
6
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/routes-8JSF7bX_.js
|
|
7
|
-
var import_jsx_runtime = require_jsx_runtime();
|
|
8
|
-
/** The dashboard route. Auth mode shows sign-in before loading owner-scoped data;
|
|
9
|
-
* open mode renders the server's shared administrative workspace. */
|
|
10
|
-
/** First-load failure screen — a calm retry instead of the router's default
|
|
11
|
-
* error dump. Only the initial loader can land here; the in-page poll is
|
|
12
|
-
* fetch-then-set and keeps stale data on a transient blip. */
|
|
13
|
-
function Home() {
|
|
14
|
-
const loaded = Route.useLoaderData();
|
|
15
|
-
const { data: session, isPending } = useSession();
|
|
16
|
-
if (loaded.auth.enabled && isPending) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("main", {
|
|
17
|
-
className: "mx-auto max-w-[1180px] px-8 pt-12",
|
|
18
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Loading, {})
|
|
19
|
-
});
|
|
20
|
-
if (loaded.auth.enabled && !session) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SignIn, {});
|
|
21
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DashboardView, {
|
|
22
|
-
initial: loaded.initial,
|
|
23
|
-
mode: loaded.auth.enabled ? "auth" : "open"
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
//#endregion
|
|
27
|
-
export { Home as component };
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
ALTER TABLE "loops" DROP CONSTRAINT "loops_agent_check";
|
|
2
|
-
--> statement-breakpoint
|
|
3
|
-
ALTER TABLE "loops" ADD CONSTRAINT "loops_agent_check"
|
|
4
|
-
CHECK ("agent" IN ('claude-code', 'codex', 'pi'));
|
|
5
|
-
--> statement-breakpoint
|
|
6
|
-
ALTER TABLE "runs" DROP CONSTRAINT "runs_agent_check";
|
|
7
|
-
--> statement-breakpoint
|
|
8
|
-
ALTER TABLE "runs" ADD CONSTRAINT "runs_agent_check"
|
|
9
|
-
CHECK ("agent" IS NULL OR "agent" IN ('claude-code', 'codex', 'pi'));
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
DROP INDEX "machines_team_idx";
|
|
2
|
-
--> statement-breakpoint
|
|
3
|
-
DROP INDEX "loops_team_idx";
|
|
4
|
-
--> statement-breakpoint
|
|
5
|
-
ALTER TABLE "machines" DROP COLUMN "team_id";
|
|
6
|
-
--> statement-breakpoint
|
|
7
|
-
ALTER TABLE "loops" DROP COLUMN "team_id";
|
|
8
|
-
--> statement-breakpoint
|
|
9
|
-
ALTER TABLE "connect_keys" DROP COLUMN "team_id";
|
|
10
|
-
--> statement-breakpoint
|
|
11
|
-
DROP TABLE "team_invites";
|
|
12
|
-
--> statement-breakpoint
|
|
13
|
-
DROP TABLE "team_members";
|
|
14
|
-
--> statement-breakpoint
|
|
15
|
-
DROP TABLE "teams";
|