Commit

Account dropdown, llms.txt onboarding, hosted agent runner (not yet deployed)

- site: shadcn-style dropdown for the account menu - llms.txt: lets an assistant register someone, wait for the confirmation email, create a token and connect; API gains /v1/register and /v1/tokens - identity: expiring access tokens for hosted attempts - crates/runner: runs Claude Code headless on an attempt, streams its events into the session, pushes and submits; secrets are redacted from everything it records and the git credential is never written to disk - services/runner/Dockerfile: the sandbox image

syntaqxcommitted Parent9b0218aBrowse files
23 files+2858−2350/23 viewed
+11−0
1+# Image builds use the repository root as their context.
2+.git
3+.credentials
4+**/node_modules
5+**/target
6+**/build
7+**/.wrangler
8+**/.dev.vars*
9+apps
10+packages
11+docs
+49−0
862862 ]
863863
864864 [[package]]
865+name = "g1t-runner"
866+version = "0.1.0"
867+dependencies = [
868+ "anyhow",
869+ "base64 0.22.1",
870+ "serde",
871+ "serde_json",
872+ "ureq",
873+]
874+
875+[[package]]
865876 name = "g1t-sshd"
866877 version = "0.1.0"
867878 dependencies = [
21282139 checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634"
21292140 dependencies = [
21302141 "aws-lc-rs",
2142+ "log",
21312143 "once_cell",
2144+ "ring",
21322145 "rustls-pki-types",
21332146 "rustls-webpki",
21342147 "subtle",
28892902 checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
28902903
28912904 [[package]]
2905+name = "ureq"
2906+version = "2.12.1"
2907+source = "registry+https://github.com/rust-lang/crates.io-index"
2908+checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
2909+dependencies = [
2910+ "base64 0.22.1",
2911+ "flate2",
2912+ "log",
2913+ "once_cell",
2914+ "rustls",
2915+ "rustls-pki-types",
2916+ "serde",
2917+ "serde_json",
2918+ "url",
2919+ "webpki-roots 0.26.11",
2920+]
2921+
2922+[[package]]
28922923 name = "url"
28932924 version = "2.5.8"
28942925 source = "registry+https://github.com/rust-lang/crates.io-index"
30493080 ]
30503081
30513082 [[package]]
3083+name = "webpki-roots"
3084+version = "0.26.11"
3085+source = "registry+https://github.com/rust-lang/crates.io-index"
3086+checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
3087+dependencies = [
3088+ "webpki-roots 1.0.9",
3089+]
3090+
3091+[[package]]
3092+name = "webpki-roots"
3093+version = "1.0.9"
3094+source = "registry+https://github.com/rust-lang/crates.io-index"
3095+checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
3096+dependencies = [
3097+ "rustls-pki-types",
3098+]
3099+
3100+[[package]]
30523101 name = "winapi-util"
30533102 version = "0.1.11"
30543103 source = "registry+https://github.com/rust-lang/crates.io-index"
+48−0
8383 await next();
8484 });
8585
86+// Onboarding. These two are REST only: they take a password, which has no
87+// place in an agent's tool call.
88+
89+async function jsonBody(request: Request): Promise<Record<string, unknown>> {
90+ try {
91+ return await request.json();
92+ } catch {
93+ return {};
94+ }
95+}
96+
97+app.post("/v1/register", async (c) => {
98+ const body = await jsonBody(c.req.raw);
99+ const result = await c.get("services").IDENTITY.register(
100+ String(body.username ?? ""),
101+ String(body.email ?? ""),
102+ String(body.password ?? ""),
103+ );
104+ if (!result.ok) {
105+ return c.json({ error: result.error }, httpStatus(result.error) as 409 | 422);
106+ }
107+ return c.json(
108+ {
109+ user: result.value.user,
110+ next: "A confirmation link was sent to that email address. The account cannot create or push anything until the link is opened.",
111+ },
112+ 201,
113+ );
114+});
115+
116+app.post("/v1/tokens", async (c) => {
117+ const body = await jsonBody(c.req.raw);
118+ const identity = c.get("services").IDENTITY;
119+ const password = String(body.password ?? "");
120+ // An existing token must not be usable to mint more tokens.
121+ const user = password.startsWith("g1t_")
122+ ? null
123+ : await identity.userForGitCredentials(String(body.username ?? ""), password);
124+ if (!user) {
125+ return c.json(
126+ { error: { code: "unauthenticated", message: "Incorrect username or password." } },
127+ 401,
128+ );
129+ }
130+ const created = await identity.createAccessToken(user, String(body.name ?? ""));
131+ return c.json({ token: created.token, verified: user.verified === true }, 201);
132+});
133+
86134 app.get("/", (c) =>
87135 c.json({
88136 name: "g1t API",
+0−195
1−import { Check, Copy } from "lucide-react";
2−import { type ComponentProps, type ReactNode, useState } from "react";
3−import { Link, type LinkProps } from "react-router";
4−
5−export function Field({
6− label,
7− hint,
8− children,
9−}: {
10− label: string;
11− hint?: string;
12− children: ReactNode;
13−}) {
14− return (
15− <label className="block">
16− <span className="mb-1.5 block text-sm font-medium text-muted">
17− {label}
18− </span>
19− {children}
20− {hint && <span className="mt-1.5 block text-xs text-faint">{hint}</span>}
21− </label>
22− );
23−}
24−
25−const CONTROL =
26− "w-full rounded-md border border-line bg-bg px-3 py-2 text-sm outline-none transition-colors placeholder:text-faint hover:border-line-strong focus:border-accent-dim";
27−
28−export function Input(props: ComponentProps<"input">) {
29− return <input {...props} className={CONTROL} />;
30−}
31−
32−export function Textarea(props: ComponentProps<"textarea">) {
33− return <textarea {...props} className={`${CONTROL} font-mono`} />;
34−}
35−
36−type Variant = "primary" | "accent" | "quiet";
37−
38−const BUTTON_BASE =
39− "inline-flex items-center justify-center gap-2 rounded-md px-3.5 py-2 text-sm font-medium transition-colors disabled:opacity-50";
40−
41−const BUTTON_VARIANTS: Record<Variant, string> = {
42− primary: "bg-fg text-bg hover:bg-white",
43− accent: "bg-accent text-bg hover:bg-[#c6ff5c]",
44− quiet:
45− "border border-line text-fg/80 hover:border-line-strong hover:bg-surface hover:text-fg",
46−};
47−
48−export function Button({
49− variant = "primary",
50− ...props
51−}: ComponentProps<"button"> & { variant?: Variant }) {
52− return (
53− <button {...props} className={`${BUTTON_BASE} ${BUTTON_VARIANTS[variant]}`} />
54− );
55−}
56−
57−/** A link that looks like a button. */
58−export function ButtonLink({
59− variant = "primary",
60− large,
61− ...props
62−}: LinkProps & { variant?: Variant; large?: boolean }) {
63− return (
64− <Link
65− {...props}
66− className={`${BUTTON_BASE} ${BUTTON_VARIANTS[variant]} ${
67− large ? "rounded-full px-5 py-2.5" : ""
68− }`}
69− />
70− );
71−}
72−
73−export function ErrorText({ children }: { children: ReactNode }) {
74− return children ? <p className="text-sm text-danger">{children}</p> : null;
75−}
76−
77−const STATUS_STYLES: Record<string, string> = {
78− open: "border-accent/30 bg-accent/10 text-accent",
79− working: "border-accent/30 bg-accent/10 text-accent",
80− submitted: "border-info/30 bg-info/10 text-info",
81− shipped: "border-shipped/30 bg-shipped/10 text-shipped",
82−};
83−
84−/** A status pill; unknown statuses render muted. */
85−export function Status({ value }: { value: string }) {
86− const style = STATUS_STYLES[value] ?? "border-line bg-surface text-muted";
87− return (
88− <span
89− className={`inline-flex shrink-0 items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs font-medium ${style}`}
90− >
91− {value === "working" && (
92− <span className="size-1.5 animate-pulse rounded-full bg-accent" />
93− )}
94− {value}
95− </span>
96− );
97−}
98−
99−/** A small outlined label. */
100−export function Pill({ children }: { children: ReactNode }) {
101− return (
102− <span className="inline-flex items-center rounded-full border border-line px-2 py-0.5 text-xs text-muted">
103− {children}
104− </span>
105− );
106−}
107−
108−const AVATAR_HUES = [82, 200, 262, 28, 330, 160];
109−
110−/** A letter avatar whose colour is stable for a given name. */
111−export function Avatar({ name, size = 20 }: { name: string; size?: number }) {
112− let hash = 0;
113− for (const char of name) hash = (hash * 31 + char.charCodeAt(0)) | 0;
114− const hue = AVATAR_HUES[Math.abs(hash) % AVATAR_HUES.length];
115− return (
116− <span
117− aria-hidden="true"
118− className="inline-flex shrink-0 items-center justify-center rounded-full font-mono font-semibold uppercase"
119− style={{
120− width: size,
121− height: size,
122− fontSize: size * 0.5,
123− background: `oklch(0.4 0.09 ${hue})`,
124− color: `oklch(0.93 0.08 ${hue})`,
125− }}
126− >
127− {name[0]}
128− </span>
129− );
130−}
131−
132−/** A line of text (usually a command) with a copy button. */
133−export function CopyLine({
134− text,
135− prompt,
136−}: {
137− text: string;
138− prompt?: boolean;
139−}) {
140− const [copied, setCopied] = useState(false);
141− return (
142− <div className="group flex items-center gap-3 rounded-lg border border-line bg-surface py-2 pr-2 pl-3.5 font-mono text-[0.8125rem]">
143− <code className="min-w-0 grow overflow-x-auto whitespace-nowrap">
144− {prompt && <span className="mr-2 text-faint select-none">$</span>}
145− {text}
146− </code>
147− <button
148− type="button"
149− aria-label="Copy"
150− className="shrink-0 rounded-md p-1.5 text-faint transition-colors hover:bg-raised hover:text-fg"
151− onClick={() => {
152− void navigator.clipboard.writeText(text);
153− setCopied(true);
154− setTimeout(() => setCopied(false), 1500);
155− }}
156− >
157− {copied ? <Check size={14} className="text-accent" /> : <Copy size={14} />}
158− </button>
159− </div>
160− );
161−}
162−
163−const UNITS: [string, number][] = [
164− ["d", 86_400_000],
165− ["h", 3_600_000],
166− ["m", 60_000],
167−];
168−
169−/** Compact relative time such as "5m ago". */
170−export function TimeAgo({ at }: { at: number }) {
171− const elapsed = Date.now() - at;
172− const unit = UNITS.find(([, size]) => elapsed >= size);
173− const label = unit ? `${Math.floor(elapsed / unit[1])}${unit[0]} ago` : "just now";
174− return (
175− // The server and the browser render at slightly different times.
176− <time dateTime={new Date(at).toISOString()} suppressHydrationWarning>
177− {label}
178− </time>
179− );
180−}
181−
182−export function EmptyState({
183− title,
184− children,
185−}: {
186− title: string;
187− children?: ReactNode;
188−}) {
189− return (
190− <div className="rounded-lg border border-dashed border-line px-6 py-12 text-center">
191− <p className="font-medium">{title}</p>
192− {children && <div className="mt-1.5 text-sm text-muted">{children}</div>}
193− </div>
194− );
195−}
+70−0
1+import { DropdownMenu as Primitive } from "radix-ui";
2+import type { ComponentProps } from "react";
3+
4+import { cn } from "../../lib/cn";
5+
6+// shadcn/ui's dropdown menu, styled with g1t's tokens.
7+
8+export const DropdownMenu = Primitive.Root;
9+export const DropdownMenuTrigger = Primitive.Trigger;
10+export const DropdownMenuGroup = Primitive.Group;
11+
12+export function DropdownMenuContent({
13+ className,
14+ sideOffset = 6,
15+ ...props
16+}: ComponentProps<typeof Primitive.Content>) {
17+ return (
18+ <Primitive.Portal>
19+ <Primitive.Content
20+ sideOffset={sideOffset}
21+ className={cn(
22+ "z-50 min-w-48 origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-lg border border-line-strong bg-raised p-1 text-sm shadow-xl shadow-black/40",
23+ className,
24+ )}
25+ {...props}
26+ />
27+ </Primitive.Portal>
28+ );
29+}
30+
31+export function DropdownMenuItem({
32+ className,
33+ ...props
34+}: ComponentProps<typeof Primitive.Item>) {
35+ return (
36+ <Primitive.Item
37+ className={cn(
38+ "flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-fg/90 outline-none select-none",
39+ "data-highlighted:bg-line data-highlighted:text-fg data-disabled:opacity-50",
40+ "[&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-faint",
41+ className,
42+ )}
43+ {...props}
44+ />
45+ );
46+}
47+
48+export function DropdownMenuLabel({
49+ className,
50+ ...props
51+}: ComponentProps<typeof Primitive.Label>) {
52+ return (
53+ <Primitive.Label
54+ className={cn("px-2 py-1.5 text-xs text-faint", className)}
55+ {...props}
56+ />
57+ );
58+}
59+
60+export function DropdownMenuSeparator({
61+ className,
62+ ...props
63+}: ComponentProps<typeof Primitive.Separator>) {
64+ return (
65+ <Primitive.Separator
66+ className={cn("-mx-1 my-1 h-px bg-line", className)}
67+ {...props}
68+ />
69+ );
70+}
+195−0
1+import { Check, Copy } from "lucide-react";
2+import { type ComponentProps, type ReactNode, useState } from "react";
3+import { Link, type LinkProps } from "react-router";
4+
5+export function Field({
6+ label,
7+ hint,
8+ children,
9+}: {
10+ label: string;
11+ hint?: string;
12+ children: ReactNode;
13+}) {
14+ return (
15+ <label className="block">
16+ <span className="mb-1.5 block text-sm font-medium text-muted">
17+ {label}
18+ </span>
19+ {children}
20+ {hint && <span className="mt-1.5 block text-xs text-faint">{hint}</span>}
21+ </label>
22+ );
23+}
24+
25+const CONTROL =
26+ "w-full rounded-md border border-line bg-bg px-3 py-2 text-sm outline-none transition-colors placeholder:text-faint hover:border-line-strong focus:border-accent-dim";
27+
28+export function Input(props: ComponentProps<"input">) {
29+ return <input {...props} className={CONTROL} />;
30+}
31+
32+export function Textarea(props: ComponentProps<"textarea">) {
33+ return <textarea {...props} className={`${CONTROL} font-mono`} />;
34+}
35+
36+type Variant = "primary" | "accent" | "quiet";
37+
38+const BUTTON_BASE =
39+ "inline-flex items-center justify-center gap-2 rounded-md px-3.5 py-2 text-sm font-medium transition-colors disabled:opacity-50";
40+
41+const BUTTON_VARIANTS: Record<Variant, string> = {
42+ primary: "bg-fg text-bg hover:bg-white",
43+ accent: "bg-accent text-bg hover:bg-[#c6ff5c]",
44+ quiet:
45+ "border border-line text-fg/80 hover:border-line-strong hover:bg-surface hover:text-fg",
46+};
47+
48+export function Button({
49+ variant = "primary",
50+ ...props
51+}: ComponentProps<"button"> & { variant?: Variant }) {
52+ return (
53+ <button {...props} className={`${BUTTON_BASE} ${BUTTON_VARIANTS[variant]}`} />
54+ );
55+}
56+
57+/** A link that looks like a button. */
58+export function ButtonLink({
59+ variant = "primary",
60+ large,
61+ ...props
62+}: LinkProps & { variant?: Variant; large?: boolean }) {
63+ return (
64+ <Link
65+ {...props}
66+ className={`${BUTTON_BASE} ${BUTTON_VARIANTS[variant]} ${
67+ large ? "rounded-full px-5 py-2.5" : ""
68+ }`}
69+ />
70+ );
71+}
72+
73+export function ErrorText({ children }: { children: ReactNode }) {
74+ return children ? <p className="text-sm text-danger">{children}</p> : null;
75+}
76+
77+const STATUS_STYLES: Record<string, string> = {
78+ open: "border-accent/30 bg-accent/10 text-accent",
79+ working: "border-accent/30 bg-accent/10 text-accent",
80+ submitted: "border-info/30 bg-info/10 text-info",
81+ shipped: "border-shipped/30 bg-shipped/10 text-shipped",
82+};
83+
84+/** A status pill; unknown statuses render muted. */
85+export function Status({ value }: { value: string }) {
86+ const style = STATUS_STYLES[value] ?? "border-line bg-surface text-muted";
87+ return (
88+ <span
89+ className={`inline-flex shrink-0 items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs font-medium ${style}`}
90+ >
91+ {value === "working" && (
92+ <span className="size-1.5 animate-pulse rounded-full bg-accent" />
93+ )}
94+ {value}
95+ </span>
96+ );
97+}
98+
99+/** A small outlined label. */
100+export function Pill({ children }: { children: ReactNode }) {
101+ return (
102+ <span className="inline-flex items-center rounded-full border border-line px-2 py-0.5 text-xs text-muted">
103+ {children}
104+ </span>
105+ );
106+}
107+
108+const AVATAR_HUES = [82, 200, 262, 28, 330, 160];
109+
110+/** A letter avatar whose colour is stable for a given name. */
111+export function Avatar({ name, size = 20 }: { name: string; size?: number }) {
112+ let hash = 0;
113+ for (const char of name) hash = (hash * 31 + char.charCodeAt(0)) | 0;
114+ const hue = AVATAR_HUES[Math.abs(hash) % AVATAR_HUES.length];
115+ return (
116+ <span
117+ aria-hidden="true"
118+ className="inline-flex shrink-0 items-center justify-center rounded-full font-mono font-semibold uppercase"
119+ style={{
120+ width: size,
121+ height: size,
122+ fontSize: size * 0.5,
123+ background: `oklch(0.4 0.09 ${hue})`,
124+ color: `oklch(0.93 0.08 ${hue})`,
125+ }}
126+ >
127+ {name[0]}
128+ </span>
129+ );
130+}
131+
132+/** A line of text (usually a command) with a copy button. */
133+export function CopyLine({
134+ text,
135+ prompt,
136+}: {
137+ text: string;
138+ prompt?: boolean;
139+}) {
140+ const [copied, setCopied] = useState(false);
141+ return (
142+ <div className="group flex items-center gap-3 rounded-lg border border-line bg-surface py-2 pr-2 pl-3.5 font-mono text-[0.8125rem]">
143+ <code className="min-w-0 grow overflow-x-auto whitespace-nowrap">
144+ {prompt && <span className="mr-2 text-faint select-none">$</span>}
145+ {text}
146+ </code>
147+ <button
148+ type="button"
149+ aria-label="Copy"
150+ className="shrink-0 rounded-md p-1.5 text-faint transition-colors hover:bg-raised hover:text-fg"
151+ onClick={() => {
152+ void navigator.clipboard.writeText(text);
153+ setCopied(true);
154+ setTimeout(() => setCopied(false), 1500);
155+ }}
156+ >
157+ {copied ? <Check size={14} className="text-accent" /> : <Copy size={14} />}
158+ </button>
159+ </div>
160+ );
161+}
162+
163+const UNITS: [string, number][] = [
164+ ["d", 86_400_000],
165+ ["h", 3_600_000],
166+ ["m", 60_000],
167+];
168+
169+/** Compact relative time such as "5m ago". */
170+export function TimeAgo({ at }: { at: number }) {
171+ const elapsed = Date.now() - at;
172+ const unit = UNITS.find(([, size]) => elapsed >= size);
173+ const label = unit ? `${Math.floor(elapsed / unit[1])}${unit[0]} ago` : "just now";
174+ return (
175+ // The server and the browser render at slightly different times.
176+ <time dateTime={new Date(at).toISOString()} suppressHydrationWarning>
177+ {label}
178+ </time>
179+ );
180+}
181+
182+export function EmptyState({
183+ title,
184+ children,
185+}: {
186+ title: string;
187+ children?: ReactNode;
188+}) {
189+ return (
190+ <div className="rounded-lg border border-dashed border-line px-6 py-12 text-center">
191+ <p className="font-medium">{title}</p>
192+ {children && <div className="mt-1.5 text-sm text-muted">{children}</div>}
193+ </div>
194+ );
195+}
+10−0
1515 Public data can be read without a token. A token that is not valid is
1616 rejected with `401` rather than treated as anonymous.
1717
18+## Getting an account and a token
19+
20+These two calls need no token, so an assistant can set someone up from
21+scratch. See [llms.txt](/llms.txt) for the full walkthrough.
22+
23+| Method | Path | |
24+| --- | --- | --- |
25+| `POST` | `/v1/register` | Create an account. Body: `username`, `email`, `password`. Sends a confirmation email. |
26+| `POST` | `/v1/tokens` | Create an access token. Body: `username`, `password`, `name`. |
27+
1828 ## Errors
1929
2030 Errors are JSON with a stable `code` and a human-readable `message`.
+4−0
66
77 This page takes you from nothing to an agent working on your repository.
88
9+Prefer to have an assistant do it? Give it [g1t.sh/llms.txt](/llms.txt) and
10+ask it to set you up. It can do everything except open the confirmation
11+email.
12+
913 ## 1. Create an account
1014
1115 [Sign up](/register) with a username, email and password. Your username is
+7−0
1+import { type ClassValue, clsx } from "clsx";
2+import { twMerge } from "tailwind-merge";
3+
4+/** Joins class names, letting later Tailwind classes override earlier ones. */
5+export function cn(...inputs: ClassValue[]): string {
6+ return twMerge(clsx(inputs));
7+}
+74−16
1−import { Plus, Search } from "lucide-react";
21 import {
2+ BookOpen,
3+ ChevronDown,
4+ LayoutDashboard,
5+ LogOut,
6+ Plus,
7+ Search,
8+ Settings,
9+ UserRound,
10+} from "lucide-react";
11+import {
312 Form,
413 isRouteErrorResponse,
514 Link,
1827 import "./app.css";
1928 import { Logo } from "./components/logo";
2029 import { Avatar, ButtonLink } from "./components/ui";
30+import {
31+ DropdownMenu,
32+ DropdownMenuContent,
33+ DropdownMenuItem,
34+ DropdownMenuLabel,
35+ DropdownMenuSeparator,
36+ DropdownMenuTrigger,
37+} from "./components/ui/dropdown-menu";
2138 import { getViewer, viewerMiddleware } from "./lib/session.server";
2239
2340 export const links: Route.LinksFunction = () => [
88105 >
89106 <Plus size={16} />
90107 </Link>
91− <Link
92− to={`/${user.username}`}
93− className="flex items-center gap-2 rounded-md px-2 py-1 text-sm hover:bg-raised"
94− >
95− <Avatar name={user.username} size={22} />
96− <span className="hidden font-mono sm:inline">{user.username}</span>
97− </Link>
98− <HeaderLink to="/settings">Settings</HeaderLink>
99− <Form method="post" action="/logout">
100− <button
101− type="submit"
102− className="rounded-md px-2.5 py-1.5 text-sm text-muted hover:bg-raised hover:text-fg"
108+ <DropdownMenu>
109+ <DropdownMenuTrigger
110+ aria-label="Account menu"
111+ className="flex items-center gap-1.5 rounded-md p-1 outline-none transition-colors hover:bg-raised data-[state=open]:bg-raised"
103112 >
104− Sign out
105− </button>
106− </Form>
113+ <Avatar name={user.username} size={24} />
114+ <ChevronDown size={14} className="text-faint" />
115+ </DropdownMenuTrigger>
116+ <DropdownMenuContent align="end" className="w-56">
117+ <DropdownMenuLabel>
118+ Signed in as{" "}
119+ <span className="font-mono font-medium text-fg">
120+ {user.username}
121+ </span>
122+ </DropdownMenuLabel>
123+ <DropdownMenuSeparator />
124+ <DropdownMenuItem asChild>
125+ <Link to={`/${user.username}`}>
126+ <UserRound />
127+ Your profile
128+ </Link>
129+ </DropdownMenuItem>
130+ <DropdownMenuItem asChild>
131+ <Link to="/">
132+ <LayoutDashboard />
133+ Mission control
134+ </Link>
135+ </DropdownMenuItem>
136+ <DropdownMenuItem asChild>
137+ <Link to="/new">
138+ <Plus />
139+ New repository
140+ </Link>
141+ </DropdownMenuItem>
142+ <DropdownMenuSeparator />
143+ <DropdownMenuItem asChild>
144+ <Link to="/settings">
145+ <Settings />
146+ Settings
147+ </Link>
148+ </DropdownMenuItem>
149+ <DropdownMenuItem asChild>
150+ <Link to="/docs">
151+ <BookOpen />
152+ Documentation
153+ </Link>
154+ </DropdownMenuItem>
155+ <DropdownMenuSeparator />
156+ <DropdownMenuItem asChild>
157+ <button type="submit" form="sign-out" className="w-full">
158+ <LogOut />
159+ Sign out
160+ </button>
161+ </DropdownMenuItem>
162+ </DropdownMenuContent>
163+ </DropdownMenu>
164+ <Form method="post" action="/logout" id="sign-out" hidden />
107165 </>
108166 ) : (
109167 <>
+5−1
1313 },
1414 "dependencies": {
1515 "@g1t/contracts": "*",
16+ "class-variance-authority": "^0.7.1",
17+ "clsx": "^2.1.1",
1618 "isbot": "^5.1.36",
1719 "lucide-react": "^1.49.0",
20+ "radix-ui": "^1.6.7",
1821 "react": "^19.2.8",
1922 "react-dom": "^19.2.8",
2023 "react-markdown": "^10.1.0",
2124 "react-router": "^8.4.0",
2225 "remark-gfm": "^4.0.1",
23− "shiki": "^4.5.0"
26+ "shiki": "^4.5.0",
27+ "tailwind-merge": "^3.7.0"
2428 },
2529 "devDependencies": {
2630 "@cloudflare/vite-plugin": "^1.62.4",
+115−0
1+# g1t
2+
3+> g1t (https://g1t.sh) is a git forge built for AI agents. It is ordinary git
4+> over HTTPS, plus a way to organise work when many agents change the same
5+> code at once. A goal is an "intent"; each agent's try at it is an "attempt"
6+> in its own fork; the owner "ships" one attempt onto main. If you know pull
7+> requests: an attempt is a pull request, an intent is the goal it serves.
8+
9+This file tells an assistant everything needed to get a person set up on g1t
10+and working. Follow the steps in order. Only step 2 needs the person.
11+
12+## Set someone up
13+
14+1. **Register.** Ask the person for a username (lowercase letters, digits and
15+ single hyphens, at most 39 characters), their email, and a password of at
16+ least 10 characters. Then:
17+
18+ ```sh
19+ curl -X POST https://api.g1t.sh/v1/register \
20+ -H "Content-Type: application/json" \
21+ -d '{"username": "USERNAME", "email": "EMAIL", "password": "PASSWORD"}'
22+ ```
23+
24+ `201` means the account exists. `409` means the username or email is
25+ taken. `422` explains what is wrong with the input. They can instead
26+ register in a browser at https://g1t.sh/register.
27+
28+2. **Confirm the email.** g1t sends a message from `noreply@g1t.sh` with a
29+ confirmation link that works for 24 hours. Ask the person to open their
30+ inbox and follow it, and wait until they say they have. Until then the
31+ account can sign in but cannot create repositories, push, or open intents;
32+ those calls return `403` with a message saying to confirm the address. If
33+ the message is missing, they can sign in at https://g1t.sh and use the
34+ "Send it again" banner.
35+
36+3. **Create an access token.**
37+
38+ ```sh
39+ curl -X POST https://api.g1t.sh/v1/tokens \
40+ -H "Content-Type: application/json" \
41+ -d '{"username": "USERNAME", "password": "PASSWORD", "name": "my laptop"}'
42+ ```
43+
44+ The response is `{"token": "g1t_…", "verified": true}`. The token is shown
45+ once. It is the password for git and the bearer token for the API and the
46+ MCP server. Store it as `G1T_TOKEN`; do not write it into a repository.
47+ If `verified` is `false`, step 2 is not finished.
48+
49+4. **Connect the MCP server** (Claude Code shown; any MCP client with HTTP
50+ transport works):
51+
52+ ```sh
53+ claude mcp add --transport http g1t https://mcp.g1t.sh \
54+ --header "Authorization: Bearer $G1T_TOKEN"
55+ ```
56+
57+5. **Push a repository.** Pushing to a repository that does not exist under
58+ the person's own username creates it, public by default.
59+
60+ ```sh
61+ git remote add g1t https://g1t.sh/USERNAME/REPO.git
62+ git -c credential.helper= \
63+ -c "http.extraHeader=Authorization: Basic $(printf '%s' "USERNAME:$G1T_TOKEN" | base64)" \
64+ push -u g1t main
65+ ```
66+
67+ Or let git ask: the username is the g1t username and the password is the
68+ token.
69+
70+## Do work
71+
72+- **Open an intent:** `POST /v1/repos/{owner}/{name}/intents` with `title`,
73+ `brief`, and optional `checks` (commands that must pass).
74+- **Start an attempt:** `POST /v1/intents/{intent_id}/attempts` with `agent`
75+ (a label such as `claude-code`). The response has `git.remote`, the
76+ attempt's own fork. Clone it, commit, and push to it with the token.
77+- **Record the session** as you work, so people can see why a change was
78+ made: `POST /v1/attempts/{attempt_id}/session` with
79+ `{"entries": [{"kind": "message", "text": "…"}]}`. Kinds are `prompt`,
80+ `message`, `tool_call`, `tool_result`, `note`. Never include secrets;
81+ sessions are as visible as the repository.
82+- **Submit:** `POST /v1/attempts/{attempt_id}/submit` with `summary`.
83+- **Ship** (repository owner only):
84+ `POST /v1/attempts/{attempt_id}/ship`. A `409` saying main has moved means
85+ the fork is behind: pull main from `https://g1t.sh/{owner}/{name}.git` into
86+ the fork, push, and ship again.
87+
88+Every one of these is also an MCP tool with the same name in snake case:
89+`open_intent`, `start_attempt`, `record_session`, `submit_attempt`,
90+`ship_attempt`, and `list_intents`, `get_intent`, `get_attempt`,
91+`read_session`, `list_repos`, `get_repo`, `create_repo`, `list_events`,
92+`whoami`.
93+
94+## Facts
95+
96+- API base: `https://api.g1t.sh`. Auth: `Authorization: Bearer g1t_…`.
97+ Public data needs no token. Errors are
98+ `{"error": {"code": "…", "message": "…"}}` with codes `unauthenticated`
99+ (401), `forbidden` (403), `not_found` (404), `conflict` (409), `invalid`
100+ (422).
101+- Git remote: `https://g1t.sh/{owner}/{repo}.git`. Attempt forks:
102+ `https://g1t.sh/attempts/{attempt_id}.git`. SSH is not available.
103+- Limits: 1 GB per repository, 32 MB per file, 100 MB per push.
104+- Forgotten password: https://g1t.sh/forgot.
105+- Not available yet: diffs and review on the site, merging on the server,
106+ running checks automatically, agents hosted by g1t, OAuth sign-in for MCP.
107+
108+## More
109+
110+- [Getting started](https://g1t.sh/docs)
111+- [Concepts](https://g1t.sh/docs/concepts)
112+- [Git](https://g1t.sh/docs/git)
113+- [Connect an agent](https://g1t.sh/docs/agents)
114+- [API reference](https://g1t.sh/docs/api)
115+- [Source](https://g1t.sh/syntaqx/g1t), MIT licensed
+5−0
9797
9898 /// `create_access_token`.
9999 #[derive(Debug, Serialize, Deserialize)]
100+#[serde(rename_all = "camelCase")]
100101 pub struct CreateAccessTokenArgs {
101102 pub user: User,
102103 pub name: String,
104+ /// When set, the token stops working after this many seconds and is
105+ /// left out of the user's token list. Used for hosted attempts.
106+ #[serde(default)]
107+ pub ttl_seconds: Option<u64>,
103108 }
104109
105110 /// The plaintext token is returned once and never stored.
+13−0
1+[package]
2+name = "g1t-runner"
3+version = "0.1.0"
4+edition.workspace = true
5+license.workspace = true
6+description = "Runs a coding agent on one attempt inside a sandbox and reports back to g1t."
7+
8+[dependencies]
9+anyhow = "1"
10+base64 = "0.22"
11+serde = { workspace = true }
12+serde_json = { workspace = true }
13+ureq = { version = "2", features = ["json"] }
+138−0
1+//! The agent harness. g1t does not implement its own agent loop: it runs
2+//! Claude Code headless and translates its event stream into session
3+//! entries.
4+
5+use std::io::{BufRead, BufReader};
6+use std::path::Path;
7+use std::process::{Command, Stdio};
8+
9+use anyhow::{Context, Result, bail};
10+use serde_json::Value;
11+
12+use crate::report::{Entry, Reporter};
13+
14+const MAX_TURNS: &str = "80";
15+
16+/// A compact, readable rendering of a tool's input.
17+fn describe_input(tool: &str, input: &Value) -> String {
18+ let field = |name: &str| input.get(name).and_then(Value::as_str);
19+ match tool {
20+ "Bash" => field("command").unwrap_or_default().to_owned(),
21+ "Read" | "Write" | "Edit" | "MultiEdit" | "NotebookEdit" => {
22+ field("file_path").unwrap_or_default().to_owned()
23+ }
24+ "Glob" | "Grep" => field("pattern").unwrap_or_default().to_owned(),
25+ _ => serde_json::to_string(input).unwrap_or_default(),
26+ }
27+}
28+
29+/// The text of a tool result, which is either a string or content blocks.
30+fn result_text(content: &Value) -> String {
31+ match content {
32+ Value::String(text) => text.clone(),
33+ Value::Array(blocks) => blocks
34+ .iter()
35+ .filter_map(|block| block.get("text").and_then(Value::as_str))
36+ .collect::<Vec<_>>()
37+ .join("\n"),
38+ _ => String::new(),
39+ }
40+}
41+
42+/// Records one line of Claude Code's `stream-json` output. Returns the final
43+/// result when the line is the one that ends the run.
44+fn handle_event(event: &Value, reporter: &mut Reporter) -> Option<Result<String>> {
45+ let blocks = || {
46+ event["message"]["content"]
47+ .as_array()
48+ .map(Vec::as_slice)
49+ .unwrap_or_default()
50+ };
51+ match event["type"].as_str()? {
52+ "assistant" => {
53+ for block in blocks() {
54+ match block["type"].as_str() {
55+ Some("text") => {
56+ let text = block["text"].as_str().unwrap_or_default().trim();
57+ if !text.is_empty() {
58+ reporter.record(Entry::new("message", text));
59+ }
60+ }
61+ Some("tool_use") => {
62+ let tool = block["name"].as_str().unwrap_or("tool");
63+ reporter.record(Entry::tool(
64+ "tool_call",
65+ tool,
66+ &describe_input(tool, &block["input"]),
67+ ));
68+ }
69+ _ => {}
70+ }
71+ }
72+ None
73+ }
74+ "user" => {
75+ for block in blocks() {
76+ if block["type"] == "tool_result" {
77+ let text = result_text(&block["content"]);
78+ if !text.trim().is_empty() {
79+ reporter.record(Entry::tool("tool_result", "result", text.trim()));
80+ }
81+ }
82+ }
83+ None
84+ }
85+ "result" => {
86+ let text = event["result"].as_str().unwrap_or_default().to_owned();
87+ Some(if event["is_error"].as_bool().unwrap_or(false) {
88+ Err(anyhow::anyhow!("the agent reported an error: {text}"))
89+ } else {
90+ Ok(text)
91+ })
92+ }
93+ _ => None,
94+ }
95+}
96+
97+/// Runs Claude Code on `prompt` in `workdir` and returns its closing
98+/// summary.
99+pub fn run_claude(workdir: &Path, prompt: &str, reporter: &mut Reporter) -> Result<String> {
100+ let mut child = Command::new("claude")
101+ .current_dir(workdir)
102+ .args([
103+ "--print",
104+ prompt,
105+ "--output-format",
106+ "stream-json",
107+ "--verbose",
108+ "--max-turns",
109+ MAX_TURNS,
110+ // The sandbox is the permission boundary: it holds one fork and
111+ // one short-lived token, and nothing else.
112+ "--dangerously-skip-permissions",
113+ ])
114+ // The agent needs the model key and nothing else of ours.
115+ .env_remove("G1T_TOKEN")
116+ .stdin(Stdio::null())
117+ .stdout(Stdio::piped())
118+ .stderr(Stdio::inherit())
119+ .spawn()
120+ .context("could not start Claude Code")?;
121+
122+ let stdout = child.stdout.take().context("no output from Claude Code")?;
123+ let mut outcome = None;
124+ for line in BufReader::new(stdout).lines() {
125+ let line = line?;
126+ let Ok(event) = serde_json::from_str::<Value>(&line) else {
127+ continue;
128+ };
129+ if let Some(result) = handle_event(&event, reporter) {
130+ outcome = Some(result);
131+ }
132+ }
133+ let status = child.wait()?;
134+ match outcome {
135+ Some(result) => result,
136+ None => bail!("Claude Code exited ({status}) without a result"),
137+ }
138+}
+138−0
1+//! Runs a coding agent on one attempt and reports back to g1t.
2+//!
3+//! This is the program a hosted sandbox starts. It clones the attempt's
4+//! fork, runs the agent harness headless, streams what the agent does into
5+//! the attempt's session as it happens, pushes the result and submits the
6+//! attempt. It talks to g1t only through the public API and git, exactly
7+//! as an agent on someone's own machine would.
8+//!
9+//! Configuration comes from the environment:
10+//!
11+//! - `G1T_API`, `G1T_TOKEN`, `G1T_USER`: where and who to report as.
12+//! - `ATTEMPT_ID`, `GIT_REMOTE`: the attempt and its fork.
13+//! - `PROMPT`: what the agent is asked to do.
14+//! - `COMMIT_MESSAGE`: used if the agent leaves changes uncommitted.
15+//! - `ANTHROPIC_API_KEY`: read by the harness itself.
16+
17+mod harness;
18+mod report;
19+
20+use std::path::Path;
21+use std::process::Command;
22+
23+use anyhow::{Context, Result, bail};
24+use base64::Engine;
25+use base64::engine::general_purpose::STANDARD;
26+
27+use report::{Entry, Reporter};
28+
29+const WORKDIR: &str = "/work/repo";
30+
31+fn env(name: &str) -> Result<String> {
32+ std::env::var(name).with_context(|| format!("{name} is not set"))
33+}
34+
35+/// Runs git and returns its trimmed output, failing on a non-zero exit.
36+fn git(dir: &Path, args: &[&str]) -> Result<String> {
37+ let output = Command::new("git")
38+ .current_dir(dir)
39+ .args(args)
40+ .output()
41+ .context("could not run git")?;
42+ if !output.status.success() {
43+ bail!(
44+ "git {} failed: {}",
45+ args.first().unwrap_or(&""),
46+ String::from_utf8_lossy(&output.stderr).trim()
47+ );
48+ }
49+ Ok(String::from_utf8_lossy(&output.stdout).trim().to_owned())
50+}
51+
52+/// A git option that authenticates one command. The credential is passed
53+/// per command and never written to the clone's config or its remote URL,
54+/// where the agent would find it.
55+fn auth_option(user: &str, token: &str) -> String {
56+ let credentials = STANDARD.encode(format!("{user}:{token}"));
57+ format!("http.extraHeader=Authorization: Basic {credentials}")
58+}
59+
60+fn run(reporter: &mut Reporter) -> Result<String> {
61+ let prompt = env("PROMPT")?;
62+ let remote = env("GIT_REMOTE")?;
63+ let auth = auth_option(&env("G1T_USER")?, &env("G1T_TOKEN")?);
64+ let workdir = Path::new(WORKDIR);
65+
66+ reporter.record(Entry::new("prompt", &prompt));
67+ reporter.flush();
68+
69+ std::fs::create_dir_all("/work")?;
70+ git(
71+ Path::new("/work"),
72+ &["-c", &auth, "clone", "--quiet", &remote, WORKDIR],
73+ )
74+ .context("could not clone the attempt's fork")?;
75+ git(workdir, &["config", "user.name", "Claude Code"])?;
76+ git(workdir, &["config", "user.email", "agent@g1t.sh"])?;
77+ let branch = git(workdir, &["rev-parse", "--abbrev-ref", "HEAD"])?;
78+ let start = git(workdir, &["rev-parse", "HEAD"]).unwrap_or_default();
79+
80+ let summary = harness::run_claude(workdir, &prompt, reporter)?;
81+
82+ // Commit whatever the agent left in the working tree.
83+ if !git(workdir, &["status", "--porcelain"])?.is_empty() {
84+ let message = std::env::var("COMMIT_MESSAGE").unwrap_or_else(|_| "Agent changes".into());
85+ git(workdir, &["add", "--all"])?;
86+ git(workdir, &["commit", "--quiet", "--message", &message])?;
87+ }
88+ let head = git(workdir, &["rev-parse", "HEAD"])?;
89+ if head == start {
90+ bail!("the agent finished without changing anything");
91+ }
92+ git(
93+ workdir,
94+ &[
95+ "-c",
96+ &auth,
97+ "push",
98+ "--quiet",
99+ "origin",
100+ &format!("HEAD:{branch}"),
101+ ],
102+ )
103+ .context("could not push the attempt's commits")?;
104+ reporter.record(Entry::new(
105+ "note",
106+ &format!(
107+ "Pushed {} to the attempt's fork.",
108+ &head[..head.len().min(12)]
109+ ),
110+ ));
111+ Ok(summary)
112+}
113+
114+fn main() {
115+ let mut reporter = match Reporter::from_env() {
116+ Ok(reporter) => reporter,
117+ Err(error) => {
118+ eprintln!("g1t-runner: {error:#}");
119+ std::process::exit(2);
120+ }
121+ };
122+ match run(&mut reporter) {
123+ Ok(summary) => {
124+ reporter.flush();
125+ if let Err(error) = reporter.submit(&summary) {
126+ eprintln!("g1t-runner: could not submit: {error:#}");
127+ std::process::exit(1);
128+ }
129+ }
130+ Err(error) => {
131+ eprintln!("g1t-runner: {error:#}");
132+ reporter.record(Entry::new("note", &format!("The run failed: {error:#}")));
133+ reporter.flush();
134+ let _ = reporter.abandon();
135+ std::process::exit(1);
136+ }
137+ }
138+}
+146−0
1+//! Reports an attempt's progress to g1t through its public API.
2+
3+use std::time::{Duration, Instant};
4+
5+use anyhow::{Context, Result};
6+use serde::Serialize;
7+
8+/// Entries are sent in batches at most this often.
9+const FLUSH_INTERVAL: Duration = Duration::from_millis(1500);
10+const FLUSH_SIZE: usize = 50;
11+/// Tool output can be enormous; the session keeps the start of it.
12+const MAX_ENTRY_CHARS: usize = 8000;
13+
14+/// One step of the session, as the API accepts it.
15+#[derive(Debug, Serialize)]
16+pub struct Entry {
17+ kind: &'static str,
18+ text: String,
19+ #[serde(skip_serializing_if = "Option::is_none")]
20+ tool: Option<String>,
21+}
22+
23+impl Entry {
24+ pub fn new(kind: &'static str, text: &str) -> Self {
25+ let mut chars = text.chars();
26+ let mut text: String = chars.by_ref().take(MAX_ENTRY_CHARS).collect();
27+ if chars.next().is_some() {
28+ text.push_str("\n… (truncated)");
29+ }
30+ Entry {
31+ kind,
32+ text,
33+ tool: None,
34+ }
35+ }
36+
37+ pub fn tool(kind: &'static str, tool: &str, text: &str) -> Self {
38+ Entry {
39+ tool: Some(tool.to_owned()),
40+ ..Entry::new(kind, text)
41+ }
42+ }
43+}
44+
45+/// Replaces every occurrence of a secret with a marker. An agent can print
46+/// its environment or a git remote; whatever it prints is recorded.
47+fn redact(text: &str, secrets: &[String]) -> String {
48+ secrets.iter().fold(text.to_owned(), |text, secret| {
49+ text.replace(secret, "[redacted]")
50+ })
51+}
52+
53+pub struct Reporter {
54+ api: String,
55+ token: String,
56+ attempt: String,
57+ /// Values that must never reach a session, which is as public as the
58+ /// repository: the credentials this process was started with.
59+ secrets: Vec<String>,
60+ pending: Vec<Entry>,
61+ last_flush: Instant,
62+}
63+
64+impl Reporter {
65+ pub fn from_env() -> Result<Self> {
66+ let var = |name: &str| std::env::var(name).with_context(|| format!("{name} is not set"));
67+ let token = var("G1T_TOKEN")?;
68+ let secrets = std::iter::once(token.clone())
69+ .chain(std::env::var("ANTHROPIC_API_KEY"))
70+ .filter(|secret| !secret.is_empty())
71+ .collect();
72+ Ok(Reporter {
73+ api: var("G1T_API")?,
74+ token,
75+ attempt: var("ATTEMPT_ID")?,
76+ secrets,
77+ pending: Vec::new(),
78+ last_flush: Instant::now(),
79+ })
80+ }
81+
82+ fn post(&self, action: &str, body: serde_json::Value) -> Result<()> {
83+ ureq::post(&format!(
84+ "{}/v1/attempts/{}/{action}",
85+ self.api, self.attempt
86+ ))
87+ .set("authorization", &format!("Bearer {}", self.token))
88+ .send_json(body)
89+ .with_context(|| format!("{action} request failed"))?;
90+ Ok(())
91+ }
92+
93+ /// Queues an entry, sending the batch if it is due.
94+ pub fn record(&mut self, mut entry: Entry) {
95+ entry.text = redact(&entry.text, &self.secrets);
96+ self.pending.push(entry);
97+ if self.pending.len() >= FLUSH_SIZE || self.last_flush.elapsed() >= FLUSH_INTERVAL {
98+ self.flush();
99+ }
100+ }
101+
102+ /// Sends everything queued. A failed send is logged and the entries
103+ /// kept, so one bad request does not lose the session or stop the run.
104+ pub fn flush(&mut self) {
105+ self.last_flush = Instant::now();
106+ if self.pending.is_empty() {
107+ return;
108+ }
109+ let body = serde_json::json!({ "entries": self.pending });
110+ match self.post("session", body) {
111+ Ok(()) => self.pending.clear(),
112+ Err(error) => eprintln!("g1t-runner: {error:#}"),
113+ }
114+ }
115+
116+ pub fn submit(&self, summary: &str) -> Result<()> {
117+ self.post("submit", serde_json::json!({ "summary": summary }))
118+ }
119+
120+ pub fn abandon(&self) -> Result<()> {
121+ self.post("abandon", serde_json::json!({}))
122+ }
123+}
124+
125+#[cfg(test)]
126+mod tests {
127+ use super::*;
128+
129+ #[test]
130+ fn secrets_are_removed_from_entries() {
131+ let secrets = vec!["g1t_abc123".to_owned(), "sk-ant-xyz".to_owned()];
132+ let text = "origin https://me:g1t_abc123@g1t.sh/a.git
133+KEY=sk-ant-xyz g1t_abc123";
134+ let clean = redact(text, &secrets);
135+ assert!(!clean.contains("g1t_abc123"));
136+ assert!(!clean.contains("sk-ant-xyz"));
137+ assert_eq!(clean.matches("[redacted]").count(), 3);
138+ }
139+
140+ #[test]
141+ fn long_entries_are_truncated() {
142+ let entry = Entry::new("note", &"x".repeat(MAX_ENTRY_CHARS + 10));
143+ assert!(entry.text.ends_with("(truncated)"));
144+ assert!(Entry::new("note", "short").text == "short");
145+ }
146+}
+1788−17
3030 "license": "MIT",
3131 "dependencies": {
3232 "@g1t/contracts": "*",
33+ "class-variance-authority": "^0.7.1",
34+ "clsx": "^2.1.1",
3335 "isbot": "^5.1.36",
3436 "lucide-react": "^1.49.0",
37+ "radix-ui": "^1.6.7",
3538 "react": "^19.2.8",
3639 "react-dom": "^19.2.8",
3740 "react-markdown": "^10.1.0",
3841 "react-router": "^8.4.0",
3942 "remark-gfm": "^4.0.1",
40− "shiki": "^4.5.0"
43+ "shiki": "^4.5.0",
44+ "tailwind-merge": "^3.7.0"
4145 },
4246 "devDependencies": {
4347 "@cloudflare/vite-plugin": "^1.62.4",
922926 "node": ">=18"
923927 }
924928 },
929+ "node_modules/@floating-ui/core": {
930+ "version": "1.8.0",
931+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz",
932+ "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==",
933+ "license": "MIT",
934+ "dependencies": {
935+ "@floating-ui/utils": "^0.2.12"
936+ }
937+ },
938+ "node_modules/@floating-ui/dom": {
939+ "version": "1.8.0",
940+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz",
941+ "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==",
942+ "license": "MIT",
943+ "dependencies": {
944+ "@floating-ui/core": "^1.8.0",
945+ "@floating-ui/utils": "^0.2.12"
946+ }
947+ },
948+ "node_modules/@floating-ui/react-dom": {
949+ "version": "2.1.9",
950+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz",
951+ "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==",
952+ "license": "MIT",
953+ "dependencies": {
954+ "@floating-ui/dom": "^1.8.0"
955+ },
956+ "peerDependencies": {
957+ "react": ">=16.8.0",
958+ "react-dom": ">=16.8.0"
959+ }
960+ },
961+ "node_modules/@floating-ui/utils": {
962+ "version": "0.2.12",
963+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz",
964+ "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==",
965+ "license": "MIT"
966+ },
925967 "node_modules/@g1t/api": {
926968 "resolved": "apps/api",
927969 "link": true
934976 "resolved": "services/events",
935977 "link": true
936978 },
937− "node_modules/@g1t/repos": {
938− "resolved": "services/repos",
939− "link": true
940− },
941979 "node_modules/@g1t/web": {
942980 "resolved": "apps/web",
943981 "link": true
16321670 "dev": true,
16331671 "license": "MIT"
16341672 },
1673+ "node_modules/@radix-ui/number": {
1674+ "version": "1.1.3",
1675+ "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.3.tgz",
1676+ "integrity": "sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==",
1677+ "license": "MIT"
1678+ },
1679+ "node_modules/@radix-ui/primitive": {
1680+ "version": "1.1.7",
1681+ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz",
1682+ "integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==",
1683+ "license": "MIT"
1684+ },
1685+ "node_modules/@radix-ui/react-accessible-icon": {
1686+ "version": "1.1.15",
1687+ "resolved": "https://registry.npmjs.org/@radix-ui/react-accessible-icon/-/react-accessible-icon-1.1.15.tgz",
1688+ "integrity": "sha512-WTQwcAvQf5sOcuUyi90lKPbhwcvQ+j55cjrSmeaN+L2vKU3DooOvlKw2MDeiJ5IkV5N905KW0/fGojKOBhD11A==",
1689+ "license": "MIT",
1690+ "dependencies": {
1691+ "@radix-ui/react-visually-hidden": "1.2.11"
1692+ },
1693+ "peerDependencies": {
1694+ "@types/react": "*",
1695+ "@types/react-dom": "*",
1696+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1697+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1698+ },
1699+ "peerDependenciesMeta": {
1700+ "@types/react": {
1701+ "optional": true
1702+ },
1703+ "@types/react-dom": {
1704+ "optional": true
1705+ }
1706+ }
1707+ },
1708+ "node_modules/@radix-ui/react-accordion": {
1709+ "version": "1.2.20",
1710+ "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.20.tgz",
1711+ "integrity": "sha512-jDhG9FvAEnlhnjrsINbNXcUa4G+L1KqSkJSunkbKEzFRcAb52jvM0PjPxPRvhe1HNc5F5yc0yzzWeeqlH4yBIg==",
1712+ "license": "MIT",
1713+ "dependencies": {
1714+ "@radix-ui/primitive": "1.1.7",
1715+ "@radix-ui/react-collapsible": "1.1.20",
1716+ "@radix-ui/react-collection": "1.1.15",
1717+ "@radix-ui/react-compose-refs": "1.1.5",
1718+ "@radix-ui/react-context": "1.2.2",
1719+ "@radix-ui/react-direction": "1.1.4",
1720+ "@radix-ui/react-id": "1.1.4",
1721+ "@radix-ui/react-primitive": "2.1.10",
1722+ "@radix-ui/react-use-controllable-state": "1.2.6"
1723+ },
1724+ "peerDependencies": {
1725+ "@types/react": "*",
1726+ "@types/react-dom": "*",
1727+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1728+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1729+ },
1730+ "peerDependenciesMeta": {
1731+ "@types/react": {
1732+ "optional": true
1733+ },
1734+ "@types/react-dom": {
1735+ "optional": true
1736+ }
1737+ }
1738+ },
1739+ "node_modules/@radix-ui/react-alert-dialog": {
1740+ "version": "1.1.23",
1741+ "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.23.tgz",
1742+ "integrity": "sha512-VAYOiQRqj3GPpYJE0I9J+X8Ip05cyVlNdKOFeiGS2Ou1HHGfpl0BxOyZm6nmVDyU+W+NF3/XLzmjHmVGydhwgA==",
1743+ "license": "MIT",
1744+ "dependencies": {
1745+ "@radix-ui/primitive": "1.1.7",
1746+ "@radix-ui/react-compose-refs": "1.1.5",
1747+ "@radix-ui/react-context": "1.2.2",
1748+ "@radix-ui/react-dialog": "1.1.23",
1749+ "@radix-ui/react-primitive": "2.1.10"
1750+ },
1751+ "peerDependencies": {
1752+ "@types/react": "*",
1753+ "@types/react-dom": "*",
1754+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1755+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1756+ },
1757+ "peerDependenciesMeta": {
1758+ "@types/react": {
1759+ "optional": true
1760+ },
1761+ "@types/react-dom": {
1762+ "optional": true
1763+ }
1764+ }
1765+ },
1766+ "node_modules/@radix-ui/react-arrow": {
1767+ "version": "1.1.15",
1768+ "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.15.tgz",
1769+ "integrity": "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==",
1770+ "license": "MIT",
1771+ "dependencies": {
1772+ "@radix-ui/react-primitive": "2.1.10"
1773+ },
1774+ "peerDependencies": {
1775+ "@types/react": "*",
1776+ "@types/react-dom": "*",
1777+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1778+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1779+ },
1780+ "peerDependenciesMeta": {
1781+ "@types/react": {
1782+ "optional": true
1783+ },
1784+ "@types/react-dom": {
1785+ "optional": true
1786+ }
1787+ }
1788+ },
1789+ "node_modules/@radix-ui/react-aspect-ratio": {
1790+ "version": "1.1.15",
1791+ "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.15.tgz",
1792+ "integrity": "sha512-fy+dyVR+90nelK8rqIznFlxzx7uPcGbhxH8Nfr2bHb4UfSe+e3hklOC0luK0hDwVwnRX7xTRySpsrQVeW+/oNQ==",
1793+ "license": "MIT",
1794+ "dependencies": {
1795+ "@radix-ui/react-primitive": "2.1.10"
1796+ },
1797+ "peerDependencies": {
1798+ "@types/react": "*",
1799+ "@types/react-dom": "*",
1800+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1801+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1802+ },
1803+ "peerDependenciesMeta": {
1804+ "@types/react": {
1805+ "optional": true
1806+ },
1807+ "@types/react-dom": {
1808+ "optional": true
1809+ }
1810+ }
1811+ },
1812+ "node_modules/@radix-ui/react-avatar": {
1813+ "version": "1.2.6",
1814+ "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.2.6.tgz",
1815+ "integrity": "sha512-4ULOTJ/mqy2hT9GlWa/MFHxHSvH3nJzHnZM1waNsc5Bonv7i70aNenghXmD97S6OJ81ekXONGGt4nT1r0PfEdA==",
1816+ "license": "MIT",
1817+ "dependencies": {
1818+ "@radix-ui/primitive": "1.1.7",
1819+ "@radix-ui/react-context": "1.2.2",
1820+ "@radix-ui/react-primitive": "2.1.10",
1821+ "@radix-ui/react-use-callback-ref": "1.1.4",
1822+ "@radix-ui/react-use-is-hydrated": "0.1.3",
1823+ "@radix-ui/react-use-layout-effect": "1.1.4"
1824+ },
1825+ "peerDependencies": {
1826+ "@types/react": "*",
1827+ "@types/react-dom": "*",
1828+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1829+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1830+ },
1831+ "peerDependenciesMeta": {
1832+ "@types/react": {
1833+ "optional": true
1834+ },
1835+ "@types/react-dom": {
1836+ "optional": true
1837+ }
1838+ }
1839+ },
1840+ "node_modules/@radix-ui/react-checkbox": {
1841+ "version": "1.3.11",
1842+ "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.11.tgz",
1843+ "integrity": "sha512-Gnptr9pDDQxD3hgq2dtPbtrp/c2qH1mBwIzw3X/ivrMb2e1t0jMTi606fVEqFPaQR1ggXIVQWKj3P2WW9v7zGQ==",
1844+ "license": "MIT",
1845+ "dependencies": {
1846+ "@radix-ui/primitive": "1.1.7",
1847+ "@radix-ui/react-compose-refs": "1.1.5",
1848+ "@radix-ui/react-context": "1.2.2",
1849+ "@radix-ui/react-presence": "1.1.10",
1850+ "@radix-ui/react-primitive": "2.1.10",
1851+ "@radix-ui/react-use-controllable-state": "1.2.6",
1852+ "@radix-ui/react-use-size": "1.1.4"
1853+ },
1854+ "peerDependencies": {
1855+ "@types/react": "*",
1856+ "@types/react-dom": "*",
1857+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1858+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1859+ },
1860+ "peerDependenciesMeta": {
1861+ "@types/react": {
1862+ "optional": true
1863+ },
1864+ "@types/react-dom": {
1865+ "optional": true
1866+ }
1867+ }
1868+ },
1869+ "node_modules/@radix-ui/react-collapsible": {
1870+ "version": "1.1.20",
1871+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.20.tgz",
1872+ "integrity": "sha512-mcGesGplBnzN2sbvJETzpCNfSMyPnb29q1GRLU+Ib7bJrpIG2ywmRoh2V5VbA2uNvKikKUlVbAPks7JDjz4A8Q==",
1873+ "license": "MIT",
1874+ "dependencies": {
1875+ "@radix-ui/primitive": "1.1.7",
1876+ "@radix-ui/react-compose-refs": "1.1.5",
1877+ "@radix-ui/react-context": "1.2.2",
1878+ "@radix-ui/react-id": "1.1.4",
1879+ "@radix-ui/react-presence": "1.1.10",
1880+ "@radix-ui/react-primitive": "2.1.10",
1881+ "@radix-ui/react-use-controllable-state": "1.2.6",
1882+ "@radix-ui/react-use-layout-effect": "1.1.4"
1883+ },
1884+ "peerDependencies": {
1885+ "@types/react": "*",
1886+ "@types/react-dom": "*",
1887+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1888+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1889+ },
1890+ "peerDependenciesMeta": {
1891+ "@types/react": {
1892+ "optional": true
1893+ },
1894+ "@types/react-dom": {
1895+ "optional": true
1896+ }
1897+ }
1898+ },
1899+ "node_modules/@radix-ui/react-collection": {
1900+ "version": "1.1.15",
1901+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.15.tgz",
1902+ "integrity": "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==",
1903+ "license": "MIT",
1904+ "dependencies": {
1905+ "@radix-ui/react-compose-refs": "1.1.5",
1906+ "@radix-ui/react-context": "1.2.2",
1907+ "@radix-ui/react-primitive": "2.1.10",
1908+ "@radix-ui/react-slot": "1.3.3"
1909+ },
1910+ "peerDependencies": {
1911+ "@types/react": "*",
1912+ "@types/react-dom": "*",
1913+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1914+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1915+ },
1916+ "peerDependenciesMeta": {
1917+ "@types/react": {
1918+ "optional": true
1919+ },
1920+ "@types/react-dom": {
1921+ "optional": true
1922+ }
1923+ }
1924+ },
1925+ "node_modules/@radix-ui/react-compose-refs": {
1926+ "version": "1.1.5",
1927+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz",
1928+ "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==",
1929+ "license": "MIT",
1930+ "peerDependencies": {
1931+ "@types/react": "*",
1932+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1933+ },
1934+ "peerDependenciesMeta": {
1935+ "@types/react": {
1936+ "optional": true
1937+ }
1938+ }
1939+ },
1940+ "node_modules/@radix-ui/react-context": {
1941+ "version": "1.2.2",
1942+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz",
1943+ "integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==",
1944+ "license": "MIT",
1945+ "peerDependencies": {
1946+ "@types/react": "*",
1947+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1948+ },
1949+ "peerDependenciesMeta": {
1950+ "@types/react": {
1951+ "optional": true
1952+ }
1953+ }
1954+ },
1955+ "node_modules/@radix-ui/react-context-menu": {
1956+ "version": "2.3.7",
1957+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.3.7.tgz",
1958+ "integrity": "sha512-CtXP35dxaB5T3zXSd+E3uHe/QpXcpYnZmxp6OaIbfthtfW4wyb77M23BG+bwIJDtsMwEP/YssdsmNyZu7jhWew==",
1959+ "license": "MIT",
1960+ "dependencies": {
1961+ "@radix-ui/primitive": "1.1.7",
1962+ "@radix-ui/react-context": "1.2.2",
1963+ "@radix-ui/react-menu": "2.1.24",
1964+ "@radix-ui/react-primitive": "2.1.10",
1965+ "@radix-ui/react-use-controllable-state": "1.2.6"
1966+ },
1967+ "peerDependencies": {
1968+ "@types/react": "*",
1969+ "@types/react-dom": "*",
1970+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1971+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1972+ },
1973+ "peerDependenciesMeta": {
1974+ "@types/react": {
1975+ "optional": true
1976+ },
1977+ "@types/react-dom": {
1978+ "optional": true
1979+ }
1980+ }
1981+ },
1982+ "node_modules/@radix-ui/react-dialog": {
1983+ "version": "1.1.23",
1984+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz",
1985+ "integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==",
1986+ "license": "MIT",
1987+ "dependencies": {
1988+ "@radix-ui/primitive": "1.1.7",
1989+ "@radix-ui/react-compose-refs": "1.1.5",
1990+ "@radix-ui/react-context": "1.2.2",
1991+ "@radix-ui/react-dismissable-layer": "1.1.19",
1992+ "@radix-ui/react-focus-guards": "1.1.6",
1993+ "@radix-ui/react-focus-scope": "1.1.16",
1994+ "@radix-ui/react-id": "1.1.4",
1995+ "@radix-ui/react-portal": "1.1.17",
1996+ "@radix-ui/react-presence": "1.1.10",
1997+ "@radix-ui/react-primitive": "2.1.10",
1998+ "@radix-ui/react-slot": "1.3.3",
1999+ "@radix-ui/react-use-controllable-state": "1.2.6",
2000+ "@radix-ui/react-use-layout-effect": "1.1.4",
2001+ "aria-hidden": "^1.2.4",
2002+ "react-remove-scroll": "^2.7.2"
2003+ },
2004+ "peerDependencies": {
2005+ "@types/react": "*",
2006+ "@types/react-dom": "*",
2007+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2008+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2009+ },
2010+ "peerDependenciesMeta": {
2011+ "@types/react": {
2012+ "optional": true
2013+ },
2014+ "@types/react-dom": {
2015+ "optional": true
2016+ }
2017+ }
2018+ },
2019+ "node_modules/@radix-ui/react-direction": {
2020+ "version": "1.1.4",
2021+ "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.4.tgz",
2022+ "integrity": "sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==",
2023+ "license": "MIT",
2024+ "peerDependencies": {
2025+ "@types/react": "*",
2026+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2027+ },
2028+ "peerDependenciesMeta": {
2029+ "@types/react": {
2030+ "optional": true
2031+ }
2032+ }
2033+ },
2034+ "node_modules/@radix-ui/react-dismissable-layer": {
2035+ "version": "1.1.19",
2036+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz",
2037+ "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==",
2038+ "license": "MIT",
2039+ "dependencies": {
2040+ "@radix-ui/primitive": "1.1.7",
2041+ "@radix-ui/react-compose-refs": "1.1.5",
2042+ "@radix-ui/react-primitive": "2.1.10",
2043+ "@radix-ui/react-use-callback-ref": "1.1.4",
2044+ "@radix-ui/react-use-effect-event": "0.0.5"
2045+ },
2046+ "peerDependencies": {
2047+ "@types/react": "*",
2048+ "@types/react-dom": "*",
2049+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2050+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2051+ },
2052+ "peerDependenciesMeta": {
2053+ "@types/react": {
2054+ "optional": true
2055+ },
2056+ "@types/react-dom": {
2057+ "optional": true
2058+ }
2059+ }
2060+ },
2061+ "node_modules/@radix-ui/react-dropdown-menu": {
2062+ "version": "2.1.24",
2063+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.24.tgz",
2064+ "integrity": "sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g==",
2065+ "license": "MIT",
2066+ "dependencies": {
2067+ "@radix-ui/primitive": "1.1.7",
2068+ "@radix-ui/react-compose-refs": "1.1.5",
2069+ "@radix-ui/react-context": "1.2.2",
2070+ "@radix-ui/react-id": "1.1.4",
2071+ "@radix-ui/react-menu": "2.1.24",
2072+ "@radix-ui/react-primitive": "2.1.10",
2073+ "@radix-ui/react-use-controllable-state": "1.2.6"
2074+ },
2075+ "peerDependencies": {
2076+ "@types/react": "*",
2077+ "@types/react-dom": "*",
2078+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2079+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2080+ },
2081+ "peerDependenciesMeta": {
2082+ "@types/react": {
2083+ "optional": true
2084+ },
2085+ "@types/react-dom": {
2086+ "optional": true
2087+ }
2088+ }
2089+ },
2090+ "node_modules/@radix-ui/react-focus-guards": {
2091+ "version": "1.1.6",
2092+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz",
2093+ "integrity": "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==",
2094+ "license": "MIT",
2095+ "peerDependencies": {
2096+ "@types/react": "*",
2097+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2098+ },
2099+ "peerDependenciesMeta": {
2100+ "@types/react": {
2101+ "optional": true
2102+ }
2103+ }
2104+ },
2105+ "node_modules/@radix-ui/react-focus-scope": {
2106+ "version": "1.1.16",
2107+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz",
2108+ "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==",
2109+ "license": "MIT",
2110+ "dependencies": {
2111+ "@radix-ui/react-compose-refs": "1.1.5",
2112+ "@radix-ui/react-primitive": "2.1.10",
2113+ "@radix-ui/react-use-callback-ref": "1.1.4"
2114+ },
2115+ "peerDependencies": {
2116+ "@types/react": "*",
2117+ "@types/react-dom": "*",
2118+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2119+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2120+ },
2121+ "peerDependenciesMeta": {
2122+ "@types/react": {
2123+ "optional": true
2124+ },
2125+ "@types/react-dom": {
2126+ "optional": true
2127+ }
2128+ }
2129+ },
2130+ "node_modules/@radix-ui/react-form": {
2131+ "version": "0.1.16",
2132+ "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.1.16.tgz",
2133+ "integrity": "sha512-Q4TLEn2A7TAypxwmd6R9EwrlXDvkfYSDMrq9/887AXAGh+G1rH+kYJKSTv+Si9Y0JPKTwKYv6PviAJosysNimA==",
2134+ "license": "MIT",
2135+ "dependencies": {
2136+ "@radix-ui/primitive": "1.1.7",
2137+ "@radix-ui/react-compose-refs": "1.1.5",
2138+ "@radix-ui/react-context": "1.2.2",
2139+ "@radix-ui/react-id": "1.1.4",
2140+ "@radix-ui/react-label": "2.1.15",
2141+ "@radix-ui/react-primitive": "2.1.10"
2142+ },
2143+ "peerDependencies": {
2144+ "@types/react": "*",
2145+ "@types/react-dom": "*",
2146+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2147+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2148+ },
2149+ "peerDependenciesMeta": {
2150+ "@types/react": {
2151+ "optional": true
2152+ },
2153+ "@types/react-dom": {
2154+ "optional": true
2155+ }
2156+ }
2157+ },
2158+ "node_modules/@radix-ui/react-hover-card": {
2159+ "version": "1.1.23",
2160+ "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.23.tgz",
2161+ "integrity": "sha512-H8qONfZd3ltrU3+jHCIgITbWo6e1iTKvP9DHdrvYbX48ooRM5FjEDTn16AMwdfuOGkWdZEhpl3PLL/Wk/AnHDQ==",
2162+ "license": "MIT",
2163+ "dependencies": {
2164+ "@radix-ui/primitive": "1.1.7",
2165+ "@radix-ui/react-compose-refs": "1.1.5",
2166+ "@radix-ui/react-context": "1.2.2",
2167+ "@radix-ui/react-dismissable-layer": "1.1.19",
2168+ "@radix-ui/react-popper": "1.3.7",
2169+ "@radix-ui/react-portal": "1.1.17",
2170+ "@radix-ui/react-presence": "1.1.10",
2171+ "@radix-ui/react-primitive": "2.1.10",
2172+ "@radix-ui/react-use-controllable-state": "1.2.6"
2173+ },
2174+ "peerDependencies": {
2175+ "@types/react": "*",
2176+ "@types/react-dom": "*",
2177+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2178+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2179+ },
2180+ "peerDependenciesMeta": {
2181+ "@types/react": {
2182+ "optional": true
2183+ },
2184+ "@types/react-dom": {
2185+ "optional": true
2186+ }
2187+ }
2188+ },
2189+ "node_modules/@radix-ui/react-id": {
2190+ "version": "1.1.4",
2191+ "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz",
2192+ "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==",
2193+ "license": "MIT",
2194+ "dependencies": {
2195+ "@radix-ui/react-use-layout-effect": "1.1.4"
2196+ },
2197+ "peerDependencies": {
2198+ "@types/react": "*",
2199+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2200+ },
2201+ "peerDependenciesMeta": {
2202+ "@types/react": {
2203+ "optional": true
2204+ }
2205+ }
2206+ },
2207+ "node_modules/@radix-ui/react-label": {
2208+ "version": "2.1.15",
2209+ "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.15.tgz",
2210+ "integrity": "sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==",
2211+ "license": "MIT",
2212+ "dependencies": {
2213+ "@radix-ui/react-primitive": "2.1.10"
2214+ },
2215+ "peerDependencies": {
2216+ "@types/react": "*",
2217+ "@types/react-dom": "*",
2218+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2219+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2220+ },
2221+ "peerDependenciesMeta": {
2222+ "@types/react": {
2223+ "optional": true
2224+ },
2225+ "@types/react-dom": {
2226+ "optional": true
2227+ }
2228+ }
2229+ },
2230+ "node_modules/@radix-ui/react-menu": {
2231+ "version": "2.1.24",
2232+ "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.24.tgz",
2233+ "integrity": "sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA==",
2234+ "license": "MIT",
2235+ "dependencies": {
2236+ "@radix-ui/primitive": "1.1.7",
2237+ "@radix-ui/react-collection": "1.1.15",
2238+ "@radix-ui/react-compose-refs": "1.1.5",
2239+ "@radix-ui/react-context": "1.2.2",
2240+ "@radix-ui/react-direction": "1.1.4",
2241+ "@radix-ui/react-dismissable-layer": "1.1.19",
2242+ "@radix-ui/react-focus-guards": "1.1.6",
2243+ "@radix-ui/react-focus-scope": "1.1.16",
2244+ "@radix-ui/react-id": "1.1.4",
2245+ "@radix-ui/react-popper": "1.3.7",
2246+ "@radix-ui/react-portal": "1.1.17",
2247+ "@radix-ui/react-presence": "1.1.10",
2248+ "@radix-ui/react-primitive": "2.1.10",
2249+ "@radix-ui/react-roving-focus": "1.1.19",
2250+ "@radix-ui/react-slot": "1.3.3",
2251+ "@radix-ui/react-use-callback-ref": "1.1.4",
2252+ "aria-hidden": "^1.2.4",
2253+ "react-remove-scroll": "^2.7.2"
2254+ },
2255+ "peerDependencies": {
2256+ "@types/react": "*",
2257+ "@types/react-dom": "*",
2258+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2259+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2260+ },
2261+ "peerDependenciesMeta": {
2262+ "@types/react": {
2263+ "optional": true
2264+ },
2265+ "@types/react-dom": {
2266+ "optional": true
2267+ }
2268+ }
2269+ },
2270+ "node_modules/@radix-ui/react-menubar": {
2271+ "version": "1.1.24",
2272+ "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.24.tgz",
2273+ "integrity": "sha512-eeVs0vf7cuqXaM0qLQCPcufImiJNVBXdJDLu7ZGYl2732UH23Qat/foNGrr6vYV3/DdTsBqASoggUFgH14OcZA==",
2274+ "license": "MIT",
2275+ "dependencies": {
2276+ "@radix-ui/primitive": "1.1.7",
2277+ "@radix-ui/react-collection": "1.1.15",
2278+ "@radix-ui/react-compose-refs": "1.1.5",
2279+ "@radix-ui/react-context": "1.2.2",
2280+ "@radix-ui/react-direction": "1.1.4",
2281+ "@radix-ui/react-id": "1.1.4",
2282+ "@radix-ui/react-menu": "2.1.24",
2283+ "@radix-ui/react-primitive": "2.1.10",
2284+ "@radix-ui/react-roving-focus": "1.1.19",
2285+ "@radix-ui/react-use-controllable-state": "1.2.6"
2286+ },
2287+ "peerDependencies": {
2288+ "@types/react": "*",
2289+ "@types/react-dom": "*",
2290+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2291+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2292+ },
2293+ "peerDependenciesMeta": {
2294+ "@types/react": {
2295+ "optional": true
2296+ },
2297+ "@types/react-dom": {
2298+ "optional": true
2299+ }
2300+ }
2301+ },
2302+ "node_modules/@radix-ui/react-navigation-menu": {
2303+ "version": "1.2.22",
2304+ "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.22.tgz",
2305+ "integrity": "sha512-ou7iLEJ+yrhQndkkA4U21XIdS/CS45F4iXIkTZcb6/Ne9EMsOuDudVmCwmDnfFZZ+y1FZqXRNSIgBy+YMvZVZg==",
2306+ "license": "MIT",
2307+ "dependencies": {
2308+ "@radix-ui/primitive": "1.1.7",
2309+ "@radix-ui/react-collection": "1.1.15",
2310+ "@radix-ui/react-compose-refs": "1.1.5",
2311+ "@radix-ui/react-context": "1.2.2",
2312+ "@radix-ui/react-direction": "1.1.4",
2313+ "@radix-ui/react-dismissable-layer": "1.1.19",
2314+ "@radix-ui/react-id": "1.1.4",
2315+ "@radix-ui/react-presence": "1.1.10",
2316+ "@radix-ui/react-primitive": "2.1.10",
2317+ "@radix-ui/react-use-callback-ref": "1.1.4",
2318+ "@radix-ui/react-use-controllable-state": "1.2.6",
2319+ "@radix-ui/react-use-layout-effect": "1.1.4",
2320+ "@radix-ui/react-use-previous": "1.1.4",
2321+ "@radix-ui/react-visually-hidden": "1.2.11"
2322+ },
2323+ "peerDependencies": {
2324+ "@types/react": "*",
2325+ "@types/react-dom": "*",
2326+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2327+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2328+ },
2329+ "peerDependenciesMeta": {
2330+ "@types/react": {
2331+ "optional": true
2332+ },
2333+ "@types/react-dom": {
2334+ "optional": true
2335+ }
2336+ }
2337+ },
2338+ "node_modules/@radix-ui/react-one-time-password-field": {
2339+ "version": "0.1.16",
2340+ "resolved": "https://registry.npmjs.org/@radix-ui/react-one-time-password-field/-/react-one-time-password-field-0.1.16.tgz",
2341+ "integrity": "sha512-Tj9P6ntAJEw52oq/F0AGknXR4XncxEt7XU47O3xJQOiWfLzEy3d9gtgKfvjSzGxzHkfL+VzvxGu2KTFsloJqXw==",
2342+ "license": "MIT",
2343+ "dependencies": {
2344+ "@radix-ui/number": "1.1.3",
2345+ "@radix-ui/primitive": "1.1.7",
2346+ "@radix-ui/react-collection": "1.1.15",
2347+ "@radix-ui/react-compose-refs": "1.1.5",
2348+ "@radix-ui/react-context": "1.2.2",
2349+ "@radix-ui/react-direction": "1.1.4",
2350+ "@radix-ui/react-primitive": "2.1.10",
2351+ "@radix-ui/react-roving-focus": "1.1.19",
2352+ "@radix-ui/react-use-controllable-state": "1.2.6",
2353+ "@radix-ui/react-use-effect-event": "0.0.5",
2354+ "@radix-ui/react-use-is-hydrated": "0.1.3",
2355+ "@radix-ui/react-use-layout-effect": "1.1.4"
2356+ },
2357+ "peerDependencies": {
2358+ "@types/react": "*",
2359+ "@types/react-dom": "*",
2360+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2361+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2362+ },
2363+ "peerDependenciesMeta": {
2364+ "@types/react": {
2365+ "optional": true
2366+ },
2367+ "@types/react-dom": {
2368+ "optional": true
2369+ }
2370+ }
2371+ },
2372+ "node_modules/@radix-ui/react-password-toggle-field": {
2373+ "version": "0.1.11",
2374+ "resolved": "https://registry.npmjs.org/@radix-ui/react-password-toggle-field/-/react-password-toggle-field-0.1.11.tgz",
2375+ "integrity": "sha512-4gvFnmDXu3dgj21CqsufzIameRvlRd4SBqaWhcrlrNhRo0Y5i/49AmRJYe1fdAM3G2VNBbmin4b0D6cdQocwgw==",
2376+ "license": "MIT",
2377+ "dependencies": {
2378+ "@radix-ui/primitive": "1.1.7",
2379+ "@radix-ui/react-compose-refs": "1.1.5",
2380+ "@radix-ui/react-context": "1.2.2",
2381+ "@radix-ui/react-id": "1.1.4",
2382+ "@radix-ui/react-primitive": "2.1.10",
2383+ "@radix-ui/react-use-controllable-state": "1.2.6",
2384+ "@radix-ui/react-use-effect-event": "0.0.5",
2385+ "@radix-ui/react-use-is-hydrated": "0.1.3"
2386+ },
2387+ "peerDependencies": {
2388+ "@types/react": "*",
2389+ "@types/react-dom": "*",
2390+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2391+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2392+ },
2393+ "peerDependenciesMeta": {
2394+ "@types/react": {
2395+ "optional": true
2396+ },
2397+ "@types/react-dom": {
2398+ "optional": true
2399+ }
2400+ }
2401+ },
2402+ "node_modules/@radix-ui/react-popover": {
2403+ "version": "1.1.23",
2404+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.23.tgz",
2405+ "integrity": "sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==",
2406+ "license": "MIT",
2407+ "dependencies": {
2408+ "@radix-ui/primitive": "1.1.7",
2409+ "@radix-ui/react-compose-refs": "1.1.5",
2410+ "@radix-ui/react-context": "1.2.2",
2411+ "@radix-ui/react-dismissable-layer": "1.1.19",
2412+ "@radix-ui/react-focus-guards": "1.1.6",
2413+ "@radix-ui/react-focus-scope": "1.1.16",
2414+ "@radix-ui/react-id": "1.1.4",
2415+ "@radix-ui/react-popper": "1.3.7",
2416+ "@radix-ui/react-portal": "1.1.17",
2417+ "@radix-ui/react-presence": "1.1.10",
2418+ "@radix-ui/react-primitive": "2.1.10",
2419+ "@radix-ui/react-slot": "1.3.3",
2420+ "@radix-ui/react-use-controllable-state": "1.2.6",
2421+ "aria-hidden": "^1.2.4",
2422+ "react-remove-scroll": "^2.7.2"
2423+ },
2424+ "peerDependencies": {
2425+ "@types/react": "*",
2426+ "@types/react-dom": "*",
2427+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2428+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2429+ },
2430+ "peerDependenciesMeta": {
2431+ "@types/react": {
2432+ "optional": true
2433+ },
2434+ "@types/react-dom": {
2435+ "optional": true
2436+ }
2437+ }
2438+ },
2439+ "node_modules/@radix-ui/react-popper": {
2440+ "version": "1.3.7",
2441+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.7.tgz",
2442+ "integrity": "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==",
2443+ "license": "MIT",
2444+ "dependencies": {
2445+ "@floating-ui/react-dom": "^2.0.0",
2446+ "@radix-ui/react-arrow": "1.1.15",
2447+ "@radix-ui/react-compose-refs": "1.1.5",
2448+ "@radix-ui/react-context": "1.2.2",
2449+ "@radix-ui/react-primitive": "2.1.10",
2450+ "@radix-ui/react-use-callback-ref": "1.1.4",
2451+ "@radix-ui/react-use-layout-effect": "1.1.4",
2452+ "@radix-ui/react-use-rect": "1.1.4",
2453+ "@radix-ui/react-use-size": "1.1.4",
2454+ "@radix-ui/rect": "1.1.3"
2455+ },
2456+ "peerDependencies": {
2457+ "@types/react": "*",
2458+ "@types/react-dom": "*",
2459+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2460+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2461+ },
2462+ "peerDependenciesMeta": {
2463+ "@types/react": {
2464+ "optional": true
2465+ },
2466+ "@types/react-dom": {
2467+ "optional": true
2468+ }
2469+ }
2470+ },
2471+ "node_modules/@radix-ui/react-portal": {
2472+ "version": "1.1.17",
2473+ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz",
2474+ "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==",
2475+ "license": "MIT",
2476+ "dependencies": {
2477+ "@radix-ui/react-primitive": "2.1.10",
2478+ "@radix-ui/react-use-layout-effect": "1.1.4"
2479+ },
2480+ "peerDependencies": {
2481+ "@types/react": "*",
2482+ "@types/react-dom": "*",
2483+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2484+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2485+ },
2486+ "peerDependenciesMeta": {
2487+ "@types/react": {
2488+ "optional": true
2489+ },
2490+ "@types/react-dom": {
2491+ "optional": true
2492+ }
2493+ }
2494+ },
2495+ "node_modules/@radix-ui/react-presence": {
2496+ "version": "1.1.10",
2497+ "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz",
2498+ "integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==",
2499+ "license": "MIT",
2500+ "dependencies": {
2501+ "@radix-ui/react-use-layout-effect": "1.1.4"
2502+ },
2503+ "peerDependencies": {
2504+ "@types/react": "*",
2505+ "@types/react-dom": "*",
2506+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2507+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2508+ },
2509+ "peerDependenciesMeta": {
2510+ "@types/react": {
2511+ "optional": true
2512+ },
2513+ "@types/react-dom": {
2514+ "optional": true
2515+ }
2516+ }
2517+ },
2518+ "node_modules/@radix-ui/react-primitive": {
2519+ "version": "2.1.10",
2520+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz",
2521+ "integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==",
2522+ "license": "MIT",
2523+ "dependencies": {
2524+ "@radix-ui/react-slot": "1.3.3"
2525+ },
2526+ "peerDependencies": {
2527+ "@types/react": "*",
2528+ "@types/react-dom": "*",
2529+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2530+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2531+ },
2532+ "peerDependenciesMeta": {
2533+ "@types/react": {
2534+ "optional": true
2535+ },
2536+ "@types/react-dom": {
2537+ "optional": true
2538+ }
2539+ }
2540+ },
2541+ "node_modules/@radix-ui/react-progress": {
2542+ "version": "1.1.16",
2543+ "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.16.tgz",
2544+ "integrity": "sha512-5XnomAsoZZCY+KNTxbIghpGqPruZvKFNlvcAljVAOdDRDsH4/OZQxhtwo5wdtoDM5R6MhJBb2sPnDuRFep3lzg==",
2545+ "license": "MIT",
2546+ "dependencies": {
2547+ "@radix-ui/react-context": "1.2.2",
2548+ "@radix-ui/react-primitive": "2.1.10"
2549+ },
2550+ "peerDependencies": {
2551+ "@types/react": "*",
2552+ "@types/react-dom": "*",
2553+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2554+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2555+ },
2556+ "peerDependenciesMeta": {
2557+ "@types/react": {
2558+ "optional": true
2559+ },
2560+ "@types/react-dom": {
2561+ "optional": true
2562+ }
2563+ }
2564+ },
2565+ "node_modules/@radix-ui/react-radio-group": {
2566+ "version": "1.4.7",
2567+ "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.4.7.tgz",
2568+ "integrity": "sha512-cgYFEkntCxppHZgtSZ+7vh0wbZQ+IC7PPMw8DSnRG27B6kDd32/Zw0OJt7dGDigCoprMuWHjg2PvUn3PYvPFoQ==",
2569+ "license": "MIT",
2570+ "dependencies": {
2571+ "@radix-ui/primitive": "1.1.7",
2572+ "@radix-ui/react-compose-refs": "1.1.5",
2573+ "@radix-ui/react-context": "1.2.2",
2574+ "@radix-ui/react-direction": "1.1.4",
2575+ "@radix-ui/react-presence": "1.1.10",
2576+ "@radix-ui/react-primitive": "2.1.10",
2577+ "@radix-ui/react-roving-focus": "1.1.19",
2578+ "@radix-ui/react-use-controllable-state": "1.2.6",
2579+ "@radix-ui/react-use-size": "1.1.4"
2580+ },
2581+ "peerDependencies": {
2582+ "@types/react": "*",
2583+ "@types/react-dom": "*",
2584+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2585+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2586+ },
2587+ "peerDependenciesMeta": {
2588+ "@types/react": {
2589+ "optional": true
2590+ },
2591+ "@types/react-dom": {
2592+ "optional": true
2593+ }
2594+ }
2595+ },
2596+ "node_modules/@radix-ui/react-roving-focus": {
2597+ "version": "1.1.19",
2598+ "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.19.tgz",
2599+ "integrity": "sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==",
2600+ "license": "MIT",
2601+ "dependencies": {
2602+ "@radix-ui/primitive": "1.1.7",
2603+ "@radix-ui/react-collection": "1.1.15",
2604+ "@radix-ui/react-compose-refs": "1.1.5",
2605+ "@radix-ui/react-context": "1.2.2",
2606+ "@radix-ui/react-direction": "1.1.4",
2607+ "@radix-ui/react-id": "1.1.4",
2608+ "@radix-ui/react-primitive": "2.1.10",
2609+ "@radix-ui/react-use-callback-ref": "1.1.4",
2610+ "@radix-ui/react-use-controllable-state": "1.2.6",
2611+ "@radix-ui/react-use-is-hydrated": "0.1.3",
2612+ "@radix-ui/react-use-layout-effect": "1.1.4"
2613+ },
2614+ "peerDependencies": {
2615+ "@types/react": "*",
2616+ "@types/react-dom": "*",
2617+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2618+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2619+ },
2620+ "peerDependenciesMeta": {
2621+ "@types/react": {
2622+ "optional": true
2623+ },
2624+ "@types/react-dom": {
2625+ "optional": true
2626+ }
2627+ }
2628+ },
2629+ "node_modules/@radix-ui/react-scroll-area": {
2630+ "version": "1.2.18",
2631+ "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.18.tgz",
2632+ "integrity": "sha512-Zn5Cd171wxsO3Dfg8HaW6RifTb9CYTKQJHs/G4+LN1GfmJpaQMZQyQxMprVPHpaz7QY4l9BxK2JwQuzHsXC8nA==",
2633+ "license": "MIT",
2634+ "dependencies": {
2635+ "@radix-ui/number": "1.1.3",
2636+ "@radix-ui/primitive": "1.1.7",
2637+ "@radix-ui/react-compose-refs": "1.1.5",
2638+ "@radix-ui/react-context": "1.2.2",
2639+ "@radix-ui/react-direction": "1.1.4",
2640+ "@radix-ui/react-presence": "1.1.10",
2641+ "@radix-ui/react-primitive": "2.1.10",
2642+ "@radix-ui/react-use-callback-ref": "1.1.4",
2643+ "@radix-ui/react-use-layout-effect": "1.1.4"
2644+ },
2645+ "peerDependencies": {
2646+ "@types/react": "*",
2647+ "@types/react-dom": "*",
2648+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2649+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2650+ },
2651+ "peerDependenciesMeta": {
2652+ "@types/react": {
2653+ "optional": true
2654+ },
2655+ "@types/react-dom": {
2656+ "optional": true
2657+ }
2658+ }
2659+ },
2660+ "node_modules/@radix-ui/react-select": {
2661+ "version": "2.3.7",
2662+ "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.3.7.tgz",
2663+ "integrity": "sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg==",
2664+ "license": "MIT",
2665+ "dependencies": {
2666+ "@radix-ui/number": "1.1.3",
2667+ "@radix-ui/primitive": "1.1.7",
2668+ "@radix-ui/react-collection": "1.1.15",
2669+ "@radix-ui/react-compose-refs": "1.1.5",
2670+ "@radix-ui/react-context": "1.2.2",
2671+ "@radix-ui/react-direction": "1.1.4",
2672+ "@radix-ui/react-dismissable-layer": "1.1.19",
2673+ "@radix-ui/react-focus-guards": "1.1.6",
2674+ "@radix-ui/react-focus-scope": "1.1.16",
2675+ "@radix-ui/react-id": "1.1.4",
2676+ "@radix-ui/react-popper": "1.3.7",
2677+ "@radix-ui/react-portal": "1.1.17",
2678+ "@radix-ui/react-presence": "1.1.10",
2679+ "@radix-ui/react-primitive": "2.1.10",
2680+ "@radix-ui/react-slot": "1.3.3",
2681+ "@radix-ui/react-use-callback-ref": "1.1.4",
2682+ "@radix-ui/react-use-controllable-state": "1.2.6",
2683+ "@radix-ui/react-use-layout-effect": "1.1.4",
2684+ "@radix-ui/react-use-previous": "1.1.4",
2685+ "@radix-ui/react-visually-hidden": "1.2.11",
2686+ "aria-hidden": "^1.2.4",
2687+ "react-remove-scroll": "^2.7.2"
2688+ },
2689+ "peerDependencies": {
2690+ "@types/react": "*",
2691+ "@types/react-dom": "*",
2692+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2693+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2694+ },
2695+ "peerDependenciesMeta": {
2696+ "@types/react": {
2697+ "optional": true
2698+ },
2699+ "@types/react-dom": {
2700+ "optional": true
2701+ }
2702+ }
2703+ },
2704+ "node_modules/@radix-ui/react-separator": {
2705+ "version": "1.1.15",
2706+ "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.15.tgz",
2707+ "integrity": "sha512-jOLO4lssEzWpoDu7G+Ze4VjwMRUBt291pnZD0gmalREZipnTX3wadQo7Fy48GCTfe14/YRN6rw/rOJqrE85Wxw==",
2708+ "license": "MIT",
2709+ "dependencies": {
2710+ "@radix-ui/react-primitive": "2.1.10"
2711+ },
2712+ "peerDependencies": {
2713+ "@types/react": "*",
2714+ "@types/react-dom": "*",
2715+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2716+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2717+ },
2718+ "peerDependenciesMeta": {
2719+ "@types/react": {
2720+ "optional": true
2721+ },
2722+ "@types/react-dom": {
2723+ "optional": true
2724+ }
2725+ }
2726+ },
2727+ "node_modules/@radix-ui/react-slider": {
2728+ "version": "1.4.7",
2729+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.4.7.tgz",
2730+ "integrity": "sha512-mTSLf1GC/C0moWjTbvCM6Qn/gBjvlFt1azuWF2v7MN5C3Zq2U2J2lN3ZEYkpujuOU5Ro7A28wkviSxaKnG0BYg==",
2731+ "license": "MIT",
2732+ "dependencies": {
2733+ "@radix-ui/number": "1.1.3",
2734+ "@radix-ui/primitive": "1.1.7",
2735+ "@radix-ui/react-collection": "1.1.15",
2736+ "@radix-ui/react-compose-refs": "1.1.5",
2737+ "@radix-ui/react-context": "1.2.2",
2738+ "@radix-ui/react-direction": "1.1.4",
2739+ "@radix-ui/react-primitive": "2.1.10",
2740+ "@radix-ui/react-use-controllable-state": "1.2.6",
2741+ "@radix-ui/react-use-layout-effect": "1.1.4",
2742+ "@radix-ui/react-use-previous": "1.1.4",
2743+ "@radix-ui/react-use-size": "1.1.4"
2744+ },
2745+ "peerDependencies": {
2746+ "@types/react": "*",
2747+ "@types/react-dom": "*",
2748+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2749+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2750+ },
2751+ "peerDependenciesMeta": {
2752+ "@types/react": {
2753+ "optional": true
2754+ },
2755+ "@types/react-dom": {
2756+ "optional": true
2757+ }
2758+ }
2759+ },
2760+ "node_modules/@radix-ui/react-slot": {
2761+ "version": "1.3.3",
2762+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz",
2763+ "integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==",
2764+ "license": "MIT",
2765+ "dependencies": {
2766+ "@radix-ui/react-compose-refs": "1.1.5"
2767+ },
2768+ "peerDependencies": {
2769+ "@types/react": "*",
2770+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2771+ },
2772+ "peerDependenciesMeta": {
2773+ "@types/react": {
2774+ "optional": true
2775+ }
2776+ }
2777+ },
2778+ "node_modules/@radix-ui/react-switch": {
2779+ "version": "1.3.7",
2780+ "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.3.7.tgz",
2781+ "integrity": "sha512-48tB/4dn2UVLBCYhTu9AuR63IHl73l/qLbLgxd86noTUor4/K4LFDAcYjK+isP5313qxaFpjPVogE7+Y0/V3Kw==",
2782+ "license": "MIT",
2783+ "dependencies": {
2784+ "@radix-ui/primitive": "1.1.7",
2785+ "@radix-ui/react-compose-refs": "1.1.5",
2786+ "@radix-ui/react-context": "1.2.2",
2787+ "@radix-ui/react-primitive": "2.1.10",
2788+ "@radix-ui/react-use-controllable-state": "1.2.6",
2789+ "@radix-ui/react-use-size": "1.1.4"
2790+ },
2791+ "peerDependencies": {
2792+ "@types/react": "*",
2793+ "@types/react-dom": "*",
2794+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2795+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2796+ },
2797+ "peerDependenciesMeta": {
2798+ "@types/react": {
2799+ "optional": true
2800+ },
2801+ "@types/react-dom": {
2802+ "optional": true
2803+ }
2804+ }
2805+ },
2806+ "node_modules/@radix-ui/react-tabs": {
2807+ "version": "1.1.21",
2808+ "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.21.tgz",
2809+ "integrity": "sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==",
2810+ "license": "MIT",
2811+ "dependencies": {
2812+ "@radix-ui/primitive": "1.1.7",
2813+ "@radix-ui/react-context": "1.2.2",
2814+ "@radix-ui/react-direction": "1.1.4",
2815+ "@radix-ui/react-id": "1.1.4",
2816+ "@radix-ui/react-presence": "1.1.10",
2817+ "@radix-ui/react-primitive": "2.1.10",
2818+ "@radix-ui/react-roving-focus": "1.1.19",
2819+ "@radix-ui/react-use-controllable-state": "1.2.6"
2820+ },
2821+ "peerDependencies": {
2822+ "@types/react": "*",
2823+ "@types/react-dom": "*",
2824+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2825+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2826+ },
2827+ "peerDependenciesMeta": {
2828+ "@types/react": {
2829+ "optional": true
2830+ },
2831+ "@types/react-dom": {
2832+ "optional": true
2833+ }
2834+ }
2835+ },
2836+ "node_modules/@radix-ui/react-toast": {
2837+ "version": "1.2.23",
2838+ "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.23.tgz",
2839+ "integrity": "sha512-ofhyAsYaocRGOs/n0XWdUOSVzEAG6BfrMVM8z0c0kLEWY38w/0WuMFPTJP/HVaZPYkMvHZoKIIhNcjbTCBILPg==",
2840+ "license": "MIT",
2841+ "dependencies": {
2842+ "@radix-ui/primitive": "1.1.7",
2843+ "@radix-ui/react-collection": "1.1.15",
2844+ "@radix-ui/react-compose-refs": "1.1.5",
2845+ "@radix-ui/react-context": "1.2.2",
2846+ "@radix-ui/react-dismissable-layer": "1.1.19",
2847+ "@radix-ui/react-portal": "1.1.17",
2848+ "@radix-ui/react-presence": "1.1.10",
2849+ "@radix-ui/react-primitive": "2.1.10",
2850+ "@radix-ui/react-use-callback-ref": "1.1.4",
2851+ "@radix-ui/react-use-controllable-state": "1.2.6",
2852+ "@radix-ui/react-use-layout-effect": "1.1.4",
2853+ "@radix-ui/react-visually-hidden": "1.2.11"
2854+ },
2855+ "peerDependencies": {
2856+ "@types/react": "*",
2857+ "@types/react-dom": "*",
2858+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2859+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2860+ },
2861+ "peerDependenciesMeta": {
2862+ "@types/react": {
2863+ "optional": true
2864+ },
2865+ "@types/react-dom": {
2866+ "optional": true
2867+ }
2868+ }
2869+ },
2870+ "node_modules/@radix-ui/react-toggle": {
2871+ "version": "1.1.18",
2872+ "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.18.tgz",
2873+ "integrity": "sha512-7lonPlKfSacd20GlOBx2ltuVKz9oqWYZz+oMQyOltw6t1y2nyftj2ZmwwUHYn49kqfDWcp8dNZm5NgV+5Z+mug==",
2874+ "license": "MIT",
2875+ "dependencies": {
2876+ "@radix-ui/primitive": "1.1.7",
2877+ "@radix-ui/react-primitive": "2.1.10",
2878+ "@radix-ui/react-use-controllable-state": "1.2.6"
2879+ },
2880+ "peerDependencies": {
2881+ "@types/react": "*",
2882+ "@types/react-dom": "*",
2883+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2884+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2885+ },
2886+ "peerDependenciesMeta": {
2887+ "@types/react": {
2888+ "optional": true
2889+ },
2890+ "@types/react-dom": {
2891+ "optional": true
2892+ }
2893+ }
2894+ },
2895+ "node_modules/@radix-ui/react-toggle-group": {
2896+ "version": "1.1.19",
2897+ "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.19.tgz",
2898+ "integrity": "sha512-OtnwuSVjd1Ofi+AdnvhsjQdyuhCDwYs1w9RyB5BN/OavXOVQo42SYqQjwUnbPnaiPFBpQ9aX70dWeee+v2oBLA==",
2899+ "license": "MIT",
2900+ "dependencies": {
2901+ "@radix-ui/primitive": "1.1.7",
2902+ "@radix-ui/react-context": "1.2.2",
2903+ "@radix-ui/react-direction": "1.1.4",
2904+ "@radix-ui/react-primitive": "2.1.10",
2905+ "@radix-ui/react-roving-focus": "1.1.19",
2906+ "@radix-ui/react-toggle": "1.1.18",
2907+ "@radix-ui/react-use-controllable-state": "1.2.6"
2908+ },
2909+ "peerDependencies": {
2910+ "@types/react": "*",
2911+ "@types/react-dom": "*",
2912+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2913+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2914+ },
2915+ "peerDependenciesMeta": {
2916+ "@types/react": {
2917+ "optional": true
2918+ },
2919+ "@types/react-dom": {
2920+ "optional": true
2921+ }
2922+ }
2923+ },
2924+ "node_modules/@radix-ui/react-toolbar": {
2925+ "version": "1.1.19",
2926+ "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.1.19.tgz",
2927+ "integrity": "sha512-Ph0IvtYw4VB12ZnZg+YtrGs8yJQsnizwo/zu0R4Y/nWugtJzA7Pg1eWeuDR9+LSqn+xjamss+UOSOJJJ4gx8jw==",
2928+ "license": "MIT",
2929+ "dependencies": {
2930+ "@radix-ui/primitive": "1.1.7",
2931+ "@radix-ui/react-context": "1.2.2",
2932+ "@radix-ui/react-direction": "1.1.4",
2933+ "@radix-ui/react-primitive": "2.1.10",
2934+ "@radix-ui/react-roving-focus": "1.1.19",
2935+ "@radix-ui/react-separator": "1.1.15",
2936+ "@radix-ui/react-toggle-group": "1.1.19"
2937+ },
2938+ "peerDependencies": {
2939+ "@types/react": "*",
2940+ "@types/react-dom": "*",
2941+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2942+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2943+ },
2944+ "peerDependenciesMeta": {
2945+ "@types/react": {
2946+ "optional": true
2947+ },
2948+ "@types/react-dom": {
2949+ "optional": true
2950+ }
2951+ }
2952+ },
2953+ "node_modules/@radix-ui/react-tooltip": {
2954+ "version": "1.2.16",
2955+ "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.16.tgz",
2956+ "integrity": "sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg==",
2957+ "license": "MIT",
2958+ "dependencies": {
2959+ "@radix-ui/primitive": "1.1.7",
2960+ "@radix-ui/react-compose-refs": "1.1.5",
2961+ "@radix-ui/react-context": "1.2.2",
2962+ "@radix-ui/react-dismissable-layer": "1.1.19",
2963+ "@radix-ui/react-id": "1.1.4",
2964+ "@radix-ui/react-popper": "1.3.7",
2965+ "@radix-ui/react-portal": "1.1.17",
2966+ "@radix-ui/react-presence": "1.1.10",
2967+ "@radix-ui/react-primitive": "2.1.10",
2968+ "@radix-ui/react-slot": "1.3.3",
2969+ "@radix-ui/react-use-controllable-state": "1.2.6",
2970+ "@radix-ui/react-use-layout-effect": "1.1.4",
2971+ "@radix-ui/react-visually-hidden": "1.2.11"
2972+ },
2973+ "peerDependencies": {
2974+ "@types/react": "*",
2975+ "@types/react-dom": "*",
2976+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
2977+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2978+ },
2979+ "peerDependenciesMeta": {
2980+ "@types/react": {
2981+ "optional": true
2982+ },
2983+ "@types/react-dom": {
2984+ "optional": true
2985+ }
2986+ }
2987+ },
2988+ "node_modules/@radix-ui/react-use-callback-ref": {
2989+ "version": "1.1.4",
2990+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz",
2991+ "integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==",
2992+ "license": "MIT",
2993+ "peerDependencies": {
2994+ "@types/react": "*",
2995+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
2996+ },
2997+ "peerDependenciesMeta": {
2998+ "@types/react": {
2999+ "optional": true
3000+ }
3001+ }
3002+ },
3003+ "node_modules/@radix-ui/react-use-controllable-state": {
3004+ "version": "1.2.6",
3005+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz",
3006+ "integrity": "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==",
3007+ "license": "MIT",
3008+ "dependencies": {
3009+ "@radix-ui/primitive": "1.1.7",
3010+ "@radix-ui/react-use-effect-event": "0.0.5",
3011+ "@radix-ui/react-use-layout-effect": "1.1.4"
3012+ },
3013+ "peerDependencies": {
3014+ "@types/react": "*",
3015+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
3016+ },
3017+ "peerDependenciesMeta": {
3018+ "@types/react": {
3019+ "optional": true
3020+ }
3021+ }
3022+ },
3023+ "node_modules/@radix-ui/react-use-effect-event": {
3024+ "version": "0.0.5",
3025+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz",
3026+ "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==",
3027+ "license": "MIT",
3028+ "dependencies": {
3029+ "@radix-ui/react-use-layout-effect": "1.1.4"
3030+ },
3031+ "peerDependencies": {
3032+ "@types/react": "*",
3033+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
3034+ },
3035+ "peerDependenciesMeta": {
3036+ "@types/react": {
3037+ "optional": true
3038+ }
3039+ }
3040+ },
3041+ "node_modules/@radix-ui/react-use-escape-keydown": {
3042+ "version": "1.1.5",
3043+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.5.tgz",
3044+ "integrity": "sha512-ge3ipobwSXTj4JyVtswQ7qZj0ZHdtbGuOno/LrgAAeSxtsJ6Vs4Gz5IkPH2bmqpjcLUFoqGhA/mueuIf63UXlA==",
3045+ "license": "MIT",
3046+ "dependencies": {
3047+ "@radix-ui/react-use-callback-ref": "1.1.4"
3048+ },
3049+ "peerDependencies": {
3050+ "@types/react": "*",
3051+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
3052+ },
3053+ "peerDependenciesMeta": {
3054+ "@types/react": {
3055+ "optional": true
3056+ }
3057+ }
3058+ },
3059+ "node_modules/@radix-ui/react-use-is-hydrated": {
3060+ "version": "0.1.3",
3061+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.3.tgz",
3062+ "integrity": "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==",
3063+ "license": "MIT",
3064+ "peerDependencies": {
3065+ "@types/react": "*",
3066+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
3067+ },
3068+ "peerDependenciesMeta": {
3069+ "@types/react": {
3070+ "optional": true
3071+ }
3072+ }
3073+ },
3074+ "node_modules/@radix-ui/react-use-layout-effect": {
3075+ "version": "1.1.4",
3076+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz",
3077+ "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==",
3078+ "license": "MIT",
3079+ "peerDependencies": {
3080+ "@types/react": "*",
3081+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
3082+ },
3083+ "peerDependenciesMeta": {
3084+ "@types/react": {
3085+ "optional": true
3086+ }
3087+ }
3088+ },
3089+ "node_modules/@radix-ui/react-use-previous": {
3090+ "version": "1.1.4",
3091+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.4.tgz",
3092+ "integrity": "sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==",
3093+ "license": "MIT",
3094+ "peerDependencies": {
3095+ "@types/react": "*",
3096+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
3097+ },
3098+ "peerDependenciesMeta": {
3099+ "@types/react": {
3100+ "optional": true
3101+ }
3102+ }
3103+ },
3104+ "node_modules/@radix-ui/react-use-rect": {
3105+ "version": "1.1.4",
3106+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.4.tgz",
3107+ "integrity": "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==",
3108+ "license": "MIT",
3109+ "dependencies": {
3110+ "@radix-ui/rect": "1.1.3"
3111+ },
3112+ "peerDependencies": {
3113+ "@types/react": "*",
3114+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
3115+ },
3116+ "peerDependenciesMeta": {
3117+ "@types/react": {
3118+ "optional": true
3119+ }
3120+ }
3121+ },
3122+ "node_modules/@radix-ui/react-use-size": {
3123+ "version": "1.1.4",
3124+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.4.tgz",
3125+ "integrity": "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==",
3126+ "license": "MIT",
3127+ "dependencies": {
3128+ "@radix-ui/react-use-layout-effect": "1.1.4"
3129+ },
3130+ "peerDependencies": {
3131+ "@types/react": "*",
3132+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
3133+ },
3134+ "peerDependenciesMeta": {
3135+ "@types/react": {
3136+ "optional": true
3137+ }
3138+ }
3139+ },
3140+ "node_modules/@radix-ui/react-visually-hidden": {
3141+ "version": "1.2.11",
3142+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.11.tgz",
3143+ "integrity": "sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==",
3144+ "license": "MIT",
3145+ "dependencies": {
3146+ "@radix-ui/react-primitive": "2.1.10"
3147+ },
3148+ "peerDependencies": {
3149+ "@types/react": "*",
3150+ "@types/react-dom": "*",
3151+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
3152+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
3153+ },
3154+ "peerDependenciesMeta": {
3155+ "@types/react": {
3156+ "optional": true
3157+ },
3158+ "@types/react-dom": {
3159+ "optional": true
3160+ }
3161+ }
3162+ },
3163+ "node_modules/@radix-ui/rect": {
3164+ "version": "1.1.3",
3165+ "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.3.tgz",
3166+ "integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==",
3167+ "license": "MIT"
3168+ },
16353169 "node_modules/@react-router/dev": {
16363170 "version": "8.4.0",
16373171 "resolved": "https://registry.npmjs.org/@react-router/dev/-/dev-8.4.0.tgz",
24864020 "version": "19.3.0",
24874021 "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz",
24884022 "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==",
2489− "dev": true,
4023+ "devOptional": true,
24904024 "license": "MIT",
24914025 "peerDependencies": {
24924026 "@types/react": "^19.3.0"
25044038 "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==",
25054039 "license": "ISC"
25064040 },
4041+ "node_modules/aria-hidden": {
4042+ "version": "1.2.6",
4043+ "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
4044+ "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
4045+ "license": "MIT",
4046+ "dependencies": {
4047+ "tslib": "^2.0.0"
4048+ },
4049+ "engines": {
4050+ "node": ">=10"
4051+ }
4052+ },
25074053 "node_modules/babel-dead-code-elimination": {
25084054 "version": "1.0.12",
25094055 "resolved": "https://registry.npmjs.org/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.12.tgz",
26684214 "url": "https://paulmillr.com/funding/"
26694215 }
26704216 },
4217+ "node_modules/class-variance-authority": {
4218+ "version": "0.7.1",
4219+ "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
4220+ "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
4221+ "license": "Apache-2.0",
4222+ "dependencies": {
4223+ "clsx": "^2.1.1"
4224+ },
4225+ "funding": {
4226+ "url": "https://polar.sh/cva"
4227+ }
4228+ },
4229+ "node_modules/clsx": {
4230+ "version": "2.1.1",
4231+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
4232+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
4233+ "license": "MIT",
4234+ "engines": {
4235+ "node": ">=6"
4236+ }
4237+ },
26714238 "node_modules/comma-separated-tokens": {
26724239 "version": "2.0.3",
26734240 "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
27824349 "node": ">=8"
27834350 }
27844351 },
4352+ "node_modules/detect-node-es": {
4353+ "version": "1.1.0",
4354+ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
4355+ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
4356+ "license": "MIT"
4357+ },
27854358 "node_modules/devlop": {
27864359 "version": "1.1.0",
27874360 "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
29764549 "node": ">=6.9.0"
29774550 }
29784551 },
4552+ "node_modules/get-nonce": {
4553+ "version": "1.0.1",
4554+ "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
4555+ "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
4556+ "license": "MIT",
4557+ "engines": {
4558+ "node": ">=6"
4559+ }
4560+ },
29794561 "node_modules/graceful-fs": {
29804562 "version": "4.2.11",
29814563 "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
45836165 "url": "https://github.com/sponsors/wooorm"
45846166 }
45856167 },
6168+ "node_modules/radix-ui": {
6169+ "version": "1.6.7",
6170+ "resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.6.7.tgz",
6171+ "integrity": "sha512-QBdhh1arIEUvPC0dQ5+nwWAxt7+N+oP/9jPwjJkGFoSk/sqxg32gJtSXGtFh8frAIcS6oC9cx2Q+7KYCQLOAeA==",
6172+ "license": "MIT",
6173+ "dependencies": {
6174+ "@radix-ui/primitive": "1.1.7",
6175+ "@radix-ui/react-accessible-icon": "1.1.15",
6176+ "@radix-ui/react-accordion": "1.2.20",
6177+ "@radix-ui/react-alert-dialog": "1.1.23",
6178+ "@radix-ui/react-arrow": "1.1.15",
6179+ "@radix-ui/react-aspect-ratio": "1.1.15",
6180+ "@radix-ui/react-avatar": "1.2.6",
6181+ "@radix-ui/react-checkbox": "1.3.11",
6182+ "@radix-ui/react-collapsible": "1.1.20",
6183+ "@radix-ui/react-collection": "1.1.15",
6184+ "@radix-ui/react-compose-refs": "1.1.5",
6185+ "@radix-ui/react-context": "1.2.2",
6186+ "@radix-ui/react-context-menu": "2.3.7",
6187+ "@radix-ui/react-dialog": "1.1.23",
6188+ "@radix-ui/react-direction": "1.1.4",
6189+ "@radix-ui/react-dismissable-layer": "1.1.19",
6190+ "@radix-ui/react-dropdown-menu": "2.1.24",
6191+ "@radix-ui/react-focus-guards": "1.1.6",
6192+ "@radix-ui/react-focus-scope": "1.1.16",
6193+ "@radix-ui/react-form": "0.1.16",
6194+ "@radix-ui/react-hover-card": "1.1.23",
6195+ "@radix-ui/react-label": "2.1.15",
6196+ "@radix-ui/react-menu": "2.1.24",
6197+ "@radix-ui/react-menubar": "1.1.24",
6198+ "@radix-ui/react-navigation-menu": "1.2.22",
6199+ "@radix-ui/react-one-time-password-field": "0.1.16",
6200+ "@radix-ui/react-password-toggle-field": "0.1.11",
6201+ "@radix-ui/react-popover": "1.1.23",
6202+ "@radix-ui/react-popper": "1.3.7",
6203+ "@radix-ui/react-portal": "1.1.17",
6204+ "@radix-ui/react-presence": "1.1.10",
6205+ "@radix-ui/react-primitive": "2.1.10",
6206+ "@radix-ui/react-progress": "1.1.16",
6207+ "@radix-ui/react-radio-group": "1.4.7",
6208+ "@radix-ui/react-roving-focus": "1.1.19",
6209+ "@radix-ui/react-scroll-area": "1.2.18",
6210+ "@radix-ui/react-select": "2.3.7",
6211+ "@radix-ui/react-separator": "1.1.15",
6212+ "@radix-ui/react-slider": "1.4.7",
6213+ "@radix-ui/react-slot": "1.3.3",
6214+ "@radix-ui/react-switch": "1.3.7",
6215+ "@radix-ui/react-tabs": "1.1.21",
6216+ "@radix-ui/react-toast": "1.2.23",
6217+ "@radix-ui/react-toggle": "1.1.18",
6218+ "@radix-ui/react-toggle-group": "1.1.19",
6219+ "@radix-ui/react-toolbar": "1.1.19",
6220+ "@radix-ui/react-tooltip": "1.2.16",
6221+ "@radix-ui/react-use-callback-ref": "1.1.4",
6222+ "@radix-ui/react-use-controllable-state": "1.2.6",
6223+ "@radix-ui/react-use-effect-event": "0.0.5",
6224+ "@radix-ui/react-use-escape-keydown": "1.1.5",
6225+ "@radix-ui/react-use-is-hydrated": "0.1.3",
6226+ "@radix-ui/react-use-layout-effect": "1.1.4",
6227+ "@radix-ui/react-use-size": "1.1.4",
6228+ "@radix-ui/react-visually-hidden": "1.2.11"
6229+ },
6230+ "peerDependencies": {
6231+ "@types/react": "*",
6232+ "@types/react-dom": "*",
6233+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
6234+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
6235+ },
6236+ "peerDependenciesMeta": {
6237+ "@types/react": {
6238+ "optional": true
6239+ },
6240+ "@types/react-dom": {
6241+ "optional": true
6242+ }
6243+ }
6244+ },
45866245 "node_modules/react": {
45876246 "version": "19.3.0",
45886247 "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz",
46416300 "node": ">=0.10.0"
46426301 }
46436302 },
6303+ "node_modules/react-remove-scroll": {
6304+ "version": "2.7.2",
6305+ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz",
6306+ "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==",
6307+ "license": "MIT",
6308+ "dependencies": {
6309+ "react-remove-scroll-bar": "^2.3.7",
6310+ "react-style-singleton": "^2.2.3",
6311+ "tslib": "^2.1.0",
6312+ "use-callback-ref": "^1.3.3",
6313+ "use-sidecar": "^1.1.3"
6314+ },
6315+ "engines": {
6316+ "node": ">=10"
6317+ },
6318+ "peerDependencies": {
6319+ "@types/react": "*",
6320+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
6321+ },
6322+ "peerDependenciesMeta": {
6323+ "@types/react": {
6324+ "optional": true
6325+ }
6326+ }
6327+ },
6328+ "node_modules/react-remove-scroll-bar": {
6329+ "version": "2.3.8",
6330+ "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
6331+ "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
6332+ "license": "MIT",
6333+ "dependencies": {
6334+ "react-style-singleton": "^2.2.2",
6335+ "tslib": "^2.0.0"
6336+ },
6337+ "engines": {
6338+ "node": ">=10"
6339+ },
6340+ "peerDependencies": {
6341+ "@types/react": "*",
6342+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
6343+ },
6344+ "peerDependenciesMeta": {
6345+ "@types/react": {
6346+ "optional": true
6347+ }
6348+ }
6349+ },
46446350 "node_modules/react-router": {
46456351 "version": "8.4.0",
46466352 "resolved": "https://registry.npmjs.org/react-router/-/react-router-8.4.0.tgz",
46636369 }
46646370 }
46656371 },
6372+ "node_modules/react-style-singleton": {
6373+ "version": "2.2.3",
6374+ "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
6375+ "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
6376+ "license": "MIT",
6377+ "dependencies": {
6378+ "get-nonce": "^1.0.0",
6379+ "tslib": "^2.0.0"
6380+ },
6381+ "engines": {
6382+ "node": ">=10"
6383+ },
6384+ "peerDependencies": {
6385+ "@types/react": "*",
6386+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
6387+ },
6388+ "peerDependenciesMeta": {
6389+ "@types/react": {
6390+ "optional": true
6391+ }
6392+ }
6393+ },
46666394 "node_modules/readdirp": {
46676395 "version": "5.1.1",
46686396 "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz",
49546682 "url": "https://github.com/chalk/supports-color?sponsor=1"
49556683 }
49566684 },
6685+ "node_modules/tailwind-merge": {
6686+ "version": "3.7.0",
6687+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.7.0.tgz",
6688+ "integrity": "sha512-XPPUyAc+cvspz3lHTcR/QgPfW2A0lv/xQNIjX3HGhLR+Nq2lHaLq5MtTesHn8GUr3W3DguT2KT5x3NVgRtYwmA==",
6689+ "license": "MIT",
6690+ "funding": {
6691+ "type": "github",
6692+ "url": "https://github.com/sponsors/dcastil"
6693+ }
6694+ },
49576695 "node_modules/tailwindcss": {
49586696 "version": "4.3.3",
49596697 "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz",
50166754 "version": "2.8.1",
50176755 "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
50186756 "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
5019− "dev": true,
5020− "license": "0BSD",
5021− "optional": true
6757+ "license": "0BSD"
50226758 },
50236759 "node_modules/typescript": {
50246760 "version": "5.9.3",
51796915 "browserslist": ">= 4.21.0"
51806916 }
51816917 },
6918+ "node_modules/use-callback-ref": {
6919+ "version": "1.3.3",
6920+ "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
6921+ "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
6922+ "license": "MIT",
6923+ "dependencies": {
6924+ "tslib": "^2.0.0"
6925+ },
6926+ "engines": {
6927+ "node": ">=10"
6928+ },
6929+ "peerDependencies": {
6930+ "@types/react": "*",
6931+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
6932+ },
6933+ "peerDependenciesMeta": {
6934+ "@types/react": {
6935+ "optional": true
6936+ }
6937+ }
6938+ },
6939+ "node_modules/use-sidecar": {
6940+ "version": "1.1.3",
6941+ "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
6942+ "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
6943+ "license": "MIT",
6944+ "dependencies": {
6945+ "detect-node-es": "^1.1.0",
6946+ "tslib": "^2.0.0"
6947+ },
6948+ "engines": {
6949+ "node": ">=10"
6950+ },
6951+ "peerDependencies": {
6952+ "@types/react": "*",
6953+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
6954+ },
6955+ "peerDependenciesMeta": {
6956+ "@types/react": {
6957+ "optional": true
6958+ }
6959+ }
6960+ },
51826961 "node_modules/valibot": {
51836962 "version": "1.5.0",
51846963 "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.5.0.tgz",
57017480 },
57027481 "services/events": {
57037482 "name": "@g1t/events",
5704− "version": "0.1.0",
5705− "license": "MIT",
5706− "dependencies": {
5707− "@g1t/contracts": "*"
5708− }
5709− },
5710− "services/repos": {
5711− "name": "@g1t/repos",
57127483 "version": "0.1.0",
57137484 "license": "MIT",
57147485 "dependencies": {
+2−1
5050 call("add_ssh_key", { user, title, publicKey }),
5151 removeSshKey: (user, id) => call("remove_ssh_key", { user, id }),
5252 listAccessTokens: (user) => call("list_access_tokens", { user }),
53− createAccessToken: (user, name) => call("create_access_token", { user, name }),
53+ createAccessToken: (user, name, ttlSeconds) =>
54+ call("create_access_token", { user, name, ttlSeconds }),
5455 removeAccessToken: (user, id) => call("remove_access_token", { user, id }),
5556 };
5657 }
+5−1
5555
5656 listAccessTokens(user: User): Promise<AccessToken[]>;
5757 /** The plaintext token is returned once and never stored. */
58− createAccessToken(user: User, name: string): Promise<{ token: string; info: AccessToken }>;
58+ /**
59+ * With `ttlSeconds` the token expires and is left out of the user's list;
60+ * that form is used for hosted attempts.
61+ */
62+ createAccessToken(user: User, name: string, ttlSeconds?: number): Promise<{ token: string; info: AccessToken }>;
5963 removeAccessToken(user: User, id: string): Promise<void>;
6064 }
+2−0
1+-- Unix seconds. Null means the token does not expire.
2+ALTER TABLE access_tokens ADD COLUMN expires_at INTEGER;
+11−4
381381 self.find_user(
382382 "SELECT users.id, users.username, users.email_verified_at IS NOT NULL AS verified FROM access_tokens
383383 JOIN users ON users.id = access_tokens.user_id
384− WHERE token_hash = ?",
384+ WHERE token_hash = ?
385+ AND (access_tokens.expires_at IS NULL OR access_tokens.expires_at > unixepoch())",
385386 &crypto::sha256_hex(token),
386387 )
387388 .await
477478 async fn list_access_tokens(&self, a: UserArgs) -> Result<Vec<AccessToken>> {
478479 let rows = self
479480 .db
480− .prepare("SELECT id, name, created_at FROM access_tokens WHERE user_id = ? ORDER BY id")
481+ // Expiring tokens belong to hosted attempts, not to the user's list.
482+ .prepare(
483+ "SELECT id, name, created_at FROM access_tokens
484+ WHERE user_id = ? AND expires_at IS NULL ORDER BY id",
485+ )
481486 .bind(&[a.user.id.into()])?
482487 .all()
483488 .await?
499504 };
500505 self.db
501506 .prepare(
502− "INSERT INTO access_tokens (id, user_id, name, token_hash, created_at)
503− VALUES (?, ?, ?, ?, ?)",
507+ "INSERT INTO access_tokens (id, user_id, name, token_hash, created_at, expires_at)
508+ VALUES (?, ?, ?, ?, ?, ?)",
504509 )
505510 .bind(&[
506511 row.id.as_str().into(),
508513 row.name.as_str().into(),
509514 crypto::sha256_hex(&token).into(),
510515 (row.created_at as f64).into(),
516+ a.ttl_seconds
517+ .map_or(JsValue::NULL, |ttl| ((row.created_at + ttl) as f64).into()),
511518 ])?
512519 .run()
513520 .await?;
+22−0
1+# The sandbox a hosted attempt runs in: git, Claude Code, and the g1t runner.
2+# Build context: the repository root.
3+
4+# The same Debian release as the runtime image, so glibc matches.
5+FROM rust:1-slim-bookworm AS build
6+WORKDIR /src
7+COPY Cargo.toml Cargo.lock ./
8+COPY crates crates
9+COPY services services
10+RUN cargo build --release --package g1t-runner
11+
12+FROM node:22-bookworm-slim
13+RUN apt-get update \
14+ && apt-get install -y --no-install-recommends git ca-certificates \
15+ && rm -rf /var/lib/apt/lists/* \
16+ && npm install --global @anthropic-ai/claude-code \
17+ && mkdir /work && chown node:node /work
18+COPY --from=build /src/target/release/g1t-runner /usr/local/bin/g1t-runner
19+# Claude Code refuses to skip permission prompts as root.
20+USER node
21+ENV HOME=/home/node
22+ENTRYPOINT ["g1t-runner"]