Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Merge branch 'worktree-agent-aaf03bdceac799c89' | 1 | import { useRouteLoaderData } from "react-router"; |
| 2 | ||
| 3 | /** | |
| 4 | * Where this g1t lives: the site, the REST API, the MCP server and the | |
| 5 | * social-card service. g1t.sh uses the defaults below; a self-hosted g1t | |
| 6 | * sets its own (app/lib/addresses.server.ts reads them), and the root | |
| 7 | * loader hands them to every page. | |
| 8 | */ | |
| 9 | export type Addresses = { | |
| 10 | /** The site's origin, such as `https://g1t.sh`. Git remotes are under it. */ | |
| 11 | site: string; | |
| 12 | /** The REST API's origin, which is also the OAuth issuer. */ | |
| 13 | api: string; | |
| 14 | /** The MCP server's URL, as agents are told to add it. */ | |
| 15 | mcp: string; | |
| 16 | /** The social-card image service's origin, or null when there is none. */ | |
| 17 | og: string | null; | |
| Repository files and avatars on g1tusercontent.com: raw files, a Raw button, images on file pages and in READMEs | 18 | /** |
| 19 | * Where bytes people supplied are served (repository files, avatars), on | |
| 20 | * an origin of its own that never sees the site's cookies. Self-hosted | |
| 21 | * without a host of its own, a path on the site (`<site>/-/usercontent`). | |
| 22 | */ | |
| 23 | usercontent: string; | |
| Merge branch 'worktree-agent-aaf03bdceac799c89' | 24 | }; |
| 25 | ||
| 26 | export const HOSTED_ADDRESSES: Addresses = { | |
| 27 | site: "https://g1t.sh", | |
| 28 | api: "https://api.g1t.sh", | |
| 29 | mcp: "https://mcp.g1t.sh", | |
| 30 | og: "https://og.g1t.sh", | |
| Repository files and avatars on g1tusercontent.com: raw files, a Raw button, images on file pages and in READMEs | 31 | usercontent: "https://g1tusercontent.com", |
| Merge branch 'worktree-agent-aaf03bdceac799c89' | 32 | }; |
| 33 | ||
| 34 | /** The Worker settings the addresses come from; every one optional. */ | |
| Repository files and avatars on g1tusercontent.com: raw files, a Raw button, images on file pages and in READMEs | 35 | export type AddressSettings = { SITE_URL?: string; API_URL?: string; MCP_URL?: string; OG_URL?: string; USERCONTENT_URL?: string }; |
| Merge branch 'worktree-agent-aaf03bdceac799c89' | 36 | |
| 37 | const trim = (value: string) => value.trim().replace(/\/+$/, ""); | |
| 38 | ||
| 39 | /** | |
| 40 | * The addresses from the Worker's settings. An unset or empty setting is | |
| 41 | * g1t.sh's address, except `OG_URL`: set to an empty string, it means there | |
| Repository files and avatars on g1tusercontent.com: raw files, a Raw button, images on file pages and in READMEs | 42 | * is no card service, and pages carry no image tags. `USERCONTENT_URL` |
| 43 | * unset is g1tusercontent.com on g1t.sh, and a path on the site wherever | |
| 44 | * `SITE_URL` names another. | |
| Merge branch 'worktree-agent-aaf03bdceac799c89' | 45 | */ |
| 46 | export function addressesFor(settings: AddressSettings): Addresses { | |
| 47 | const pick = (value: string | undefined, fallback: string) => (value ? trim(value) : "") || fallback; | |
| Repository files and avatars on g1tusercontent.com: raw files, a Raw button, images on file pages and in READMEs | 48 | const site = pick(settings.SITE_URL, HOSTED_ADDRESSES.site); |
| 49 | const ownSite = site === HOSTED_ADDRESSES.site; | |
| Merge branch 'worktree-agent-aaf03bdceac799c89' | 50 | return { |
| Repository files and avatars on g1tusercontent.com: raw files, a Raw button, images on file pages and in READMEs | 51 | site, |
| 52 | usercontent: usercontentOf(pick(settings.USERCONTENT_URL, ownSite ? HOSTED_ADDRESSES.usercontent : `${site}/-/usercontent`), site), | |
| Merge branch 'worktree-agent-aaf03bdceac799c89' | 53 | api: pick(settings.API_URL, HOSTED_ADDRESSES.api), |
| 54 | mcp: pick(settings.MCP_URL, HOSTED_ADDRESSES.mcp), | |
| 55 | og: settings.OG_URL === undefined ? HOSTED_ADDRESSES.og : trim(settings.OG_URL) || null, | |
| 56 | }; | |
| 57 | } | |
| 58 | ||
| Repository files and avatars on g1tusercontent.com: raw files, a Raw button, images on file pages and in READMEs | 59 | /** |
| 60 | * Where uploaded avatars and repository files are served, from the root | |
| 61 | * loader's data; the site's own paths when it has none (they redirect). | |
| 62 | */ | |
| 63 | export function usercontentFrom(rootData: unknown): string { | |
| 64 | return (rootData as { addresses?: Addresses } | null | undefined)?.addresses?.usercontent ?? ""; | |
| 65 | } | |
| 66 | ||
| 67 | /** The site's own origin is never the usercontent origin: a path on it is. */ | |
| 68 | function usercontentOf(address: string, site: string): string { | |
| 69 | return address === site ? `${site}/-/usercontent` : address; | |
| 70 | } | |
| 71 | ||
| Merge branch 'worktree-agent-aaf03bdceac799c89' | 72 | /** The addresses in the root loader's data, or g1t.sh's when it has none. */ |
| 73 | export function addressesFrom(rootData: unknown): Addresses { | |
| 74 | return (rootData as { addresses?: Addresses } | null | undefined)?.addresses ?? HOSTED_ADDRESSES; | |
| 75 | } | |
| 76 | ||
| 77 | /** This g1t's addresses, from the root loader. */ | |
| 78 | export function useAddresses(): Addresses { | |
| 79 | return addressesFrom(useRouteLoaderData("root")); | |
| 80 | } | |
| 81 | ||
| Code button: the SSH tab shows the address it will have, disabled, with why it is not on yet | 82 | /** The SSH clone address of a repository (`git@host:owner/name.git`), from its `owner/name`. */ |
| 83 | export function sshUrl(addresses: Pick<Addresses, "site">, path: string): string { | |
| 84 | return `git@${new URL(addresses.site).hostname}:${path.replace(/^\/+/, "")}.git`; | |
| 85 | } | |
| 86 | ||
| Merge branch 'worktree-agent-aaf03bdceac799c89' | 87 | /** The HTTPS clone address of a repository, from its `owner/name`. */ |
| 88 | export function cloneUrl(addresses: Pick<Addresses, "site">, path: string): string { | |
| 89 | return `${addresses.site}/${path.replace(/^\/+/, "")}.git`; | |
| 90 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.