feat: versioned docs with v1.0.5 archive and v2.0-alpha current

This commit is contained in:
2026-03-16 07:07:35 +00:00
37 changed files with 1959 additions and 226 deletions

View File

@@ -1,6 +1,7 @@
import { defineConfig } from 'astro/config'
import starlight from '@astrojs/starlight'
import tailwindcss from '@tailwindcss/vite'
import starlightVersions from 'starlight-versions'
// SEE: https://developers.google.com/analytics/devguides/collection/gtagjs
const gaTrackingID = import.meta.env.GA_TRACKING_ID
@@ -107,6 +108,12 @@ head.push(
export default defineConfig({
integrations: [
starlight({
plugins: [
starlightVersions({
current: { label: 'v2.0-alpha' },
versions: [{ slug: '1.0', label: 'v1.0.5' }],
}),
],
title: 'grlx Docs',
lastUpdated: true,
description:
@@ -126,10 +133,22 @@ export default defineConfig({
head,
logo: { src: './src/assets/grlx.webp' },
social: [
{ icon: 'github', label: 'GitHub', href: 'https://github.com/gogrlx/grlx' },
{
icon: 'github',
label: 'GitHub',
href: 'https://github.com/gogrlx/grlx',
},
{ icon: 'x.com', label: 'X', href: 'https://x.com/gogrlx' },
{ icon: 'discord', label: 'Discord', href: 'https://discord.gg/RNsZ3KWjXm' },
{ icon: 'email', label: 'Email', href: 'mailto:grlx@adatomic.com?subject=Question' },
{
icon: 'discord',
label: 'Discord',
href: 'https://discord.gg/RNsZ3KWjXm',
},
{
icon: 'email',
label: 'Email',
href: 'mailto:grlx@adatomic.com?subject=Question',
},
],
sidebar: [
{ label: 'Getting Started', link: '/getting-started' },
@@ -137,6 +156,8 @@ export default defineConfig({
label: 'Architecture',
autogenerate: { directory: 'architecture' },
},
{ label: 'Cohorts', link: '/cohorts' },
{ label: 'Managing Sprouts', link: '/sprouts' },
{
label: 'Ingredients',
autogenerate: { directory: 'ingredients' },
@@ -145,6 +166,14 @@ export default defineConfig({
label: 'Recipes',
autogenerate: { directory: 'recipes' },
},
{ label: 'SSH', link: '/ssh' },
{
label: 'Security',
items: [
{ label: 'RBAC', link: '/rbac' },
{ label: 'Audit Logging', link: '/audit' },
],
},
{ label: 'Glossary', link: '/glossary' },
],
}),

241
bun.lock
View File

@@ -5,20 +5,21 @@
"": {
"name": "docs.grlx.dev",
"dependencies": {
"@astrojs/starlight": "^0.37.6",
"@astrojs/starlight-tailwind": "^4.0.2",
"@astrojs/starlight": "^0.38.1",
"@astrojs/starlight-tailwind": "^5.0.0",
"@fontsource/ibm-plex-mono": "^5.2.7",
"@fontsource/ibm-plex-sans": "^5.2.8",
"@fontsource/ibm-plex-serif": "^5.2.7",
"@tailwindcss/vite": "^4.2.1",
"astro": "^5.18.0",
"astro": "^6.0.4",
"sharp": "^0.34.5",
"starlight-versions": "^0.8.1",
"tailwindcss": "^4.2.1",
},
"devDependencies": {
"@typescript-eslint/parser": "^8.56.1",
"@typescript-eslint/parser": "^8.57.0",
"astro-eslint-parser": "^1.3.0",
"eslint": "^9.39.3",
"eslint": "^10.0.3",
"eslint-plugin-astro": "^1.6.0",
"prettier": "^3.8.1",
"prettier-plugin-astro": "^0.14.1",
@@ -30,19 +31,19 @@
"packages": {
"@astrojs/compiler": ["@astrojs/compiler@2.13.1", "", {}, "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg=="],
"@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.5", "", {}, "sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA=="],
"@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.8.0", "", { "dependencies": { "picomatch": "^4.0.3" } }, "sha512-J56GrhEiV+4dmrGLPNOl2pZjpHXAndWVyiVDYGDuw6MWKpBSEMLdFxHzeM/6sqaknw9M+HFfHZAcvi3OfT3D/w=="],
"@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.10", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.5", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.19.0", "smol-toml": "^1.5.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A=="],
"@astrojs/markdown-remark": ["@astrojs/markdown-remark@7.0.0", "", { "dependencies": { "@astrojs/internal-helpers": "0.8.0", "@astrojs/prism": "4.0.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^4.0.0", "smol-toml": "^1.6.0", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-jTAXHPy45L7o1ljH4jYV+ShtOHtyQUa1mGp3a5fJp1soX8lInuTJQ6ihmldHzVM4Q7QptU4SzIDIcKbBJO7sXQ=="],
"@astrojs/mdx": ["@astrojs/mdx@4.3.13", "", { "dependencies": { "@astrojs/markdown-remark": "6.3.10", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.15.0", "es-module-lexer": "^1.7.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "piccolore": "^0.1.3", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-IHDHVKz0JfKBy3//52JSiyWv089b7GVSChIXLrlUOoTLWowG3wr2/8hkaEgEyd/vysvNQvGk+QhysXpJW5ve6Q=="],
"@astrojs/mdx": ["@astrojs/mdx@5.0.0", "", { "dependencies": { "@astrojs/markdown-remark": "7.0.0", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.16.0", "es-module-lexer": "^2.0.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "piccolore": "^0.1.3", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { "astro": "^6.0.0-alpha.0" } }, "sha512-J4rW6eT+qgVw7+RXdBYO4vYyWGeXXQp8wop9dXsOlLzIsVSxyttMCgkGCWvIR2ogBqKqeYgI6YDW93PaDHkCaA=="],
"@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="],
"@astrojs/prism": ["@astrojs/prism@4.0.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-NndtNPpxaGinRpRytljGBvYHpTOwHycSZ/c+lQi5cHvkqqrHKWdkPEhImlODBNmbuB+vyQUNUDXyjzt66CihJg=="],
"@astrojs/sitemap": ["@astrojs/sitemap@3.7.0", "", { "dependencies": { "sitemap": "^8.0.2", "stream-replace-string": "^2.0.0", "zod": "^3.25.76" } }, "sha512-+qxjUrz6Jcgh+D5VE1gKUJTA3pSthuPHe6Ao5JCxok794Lewx8hBFaWHtOnN0ntb2lfOf7gvOi9TefUswQ/ZVA=="],
"@astrojs/sitemap": ["@astrojs/sitemap@3.7.1", "", { "dependencies": { "sitemap": "^9.0.0", "stream-replace-string": "^2.0.0", "zod": "^4.3.6" } }, "sha512-IzQqdTeskaMX+QDZCzMuJIp8A8C1vgzMBp/NmHNnadepHYNHcxQdGLQZYfkbd2EbRXUfOS+UDIKx8sKg0oWVdw=="],
"@astrojs/starlight": ["@astrojs/starlight@0.37.6", "", { "dependencies": { "@astrojs/markdown-remark": "^6.3.1", "@astrojs/mdx": "^4.2.3", "@astrojs/sitemap": "^3.3.0", "@pagefind/default-ui": "^1.3.0", "@types/hast": "^3.0.4", "@types/js-yaml": "^4.0.9", "@types/mdast": "^4.0.4", "astro-expressive-code": "^0.41.1", "bcp-47": "^2.1.0", "hast-util-from-html": "^2.0.1", "hast-util-select": "^6.0.2", "hast-util-to-string": "^3.0.0", "hastscript": "^9.0.0", "i18next": "^23.11.5", "js-yaml": "^4.1.0", "klona": "^2.0.6", "magic-string": "^0.30.17", "mdast-util-directive": "^3.0.0", "mdast-util-to-markdown": "^2.1.0", "mdast-util-to-string": "^4.0.0", "pagefind": "^1.3.0", "rehype": "^13.0.1", "rehype-format": "^5.0.0", "remark-directive": "^3.0.0", "ultrahtml": "^1.6.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "vfile": "^6.0.2" }, "peerDependencies": { "astro": "^5.5.0" } }, "sha512-wQrKwH431q+8FsLBnNQeG+R36TMtEGxTQ2AuiVpcx9APcazvL3n7wVW8mMmYyxX0POjTnxlcWPkdMGR3Yj1L+w=="],
"@astrojs/starlight": ["@astrojs/starlight@0.38.1", "", { "dependencies": { "@astrojs/markdown-remark": "^7.0.0", "@astrojs/mdx": "^5.0.0", "@astrojs/sitemap": "^3.7.1", "@pagefind/default-ui": "^1.3.0", "@types/hast": "^3.0.4", "@types/js-yaml": "^4.0.9", "@types/mdast": "^4.0.4", "astro-expressive-code": "^0.41.6", "bcp-47": "^2.1.0", "hast-util-from-html": "^2.0.1", "hast-util-select": "^6.0.2", "hast-util-to-string": "^3.0.0", "hastscript": "^9.0.0", "i18next": "^23.11.5", "js-yaml": "^4.1.0", "klona": "^2.0.6", "magic-string": "^0.30.17", "mdast-util-directive": "^3.0.0", "mdast-util-to-markdown": "^2.1.0", "mdast-util-to-string": "^4.0.0", "pagefind": "^1.3.0", "rehype": "^13.0.1", "rehype-format": "^5.0.0", "remark-directive": "^3.0.0", "ultrahtml": "^1.6.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "vfile": "^6.0.2" }, "peerDependencies": { "astro": "^6.0.0" } }, "sha512-CATPH4Dy44OYAJhoyUHh6NqpColWEVufanGVwnM0l/bcaNMo5V/rypwL0Vu0Edp+ZIXE7/1DA9CrNj5jmCVSLQ=="],
"@astrojs/starlight-tailwind": ["@astrojs/starlight-tailwind@4.0.2", "", { "peerDependencies": { "@astrojs/starlight": ">=0.34.0", "tailwindcss": "^4.0.0" } }, "sha512-SYN/6zq6hJO5tWqbQ2tWT9/jd8ubUkzkBCcF94vByC/ZJ20Mi5GPjFvAh89Yky/aIM+jXxT6W5q4p6l58GKHiQ=="],
"@astrojs/starlight-tailwind": ["@astrojs/starlight-tailwind@5.0.0", "", { "peerDependencies": { "@astrojs/starlight": ">=0.38.0", "tailwindcss": "^4.0.0" } }, "sha512-VivF+bWg++4ma/ffr5sgHsd/ONtGdVJIKAaRZ6jmL4yqxy7bviu59MGNi5aW3nd8psP9i/aivBTrpwGxRM1XyA=="],
"@astrojs/telemetry": ["@astrojs/telemetry@3.3.0", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ=="],
@@ -58,6 +59,10 @@
"@capsizecss/unpack": ["@capsizecss/unpack@4.0.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA=="],
"@clack/core": ["@clack/core@1.1.0", "", { "dependencies": { "sisteransi": "^1.0.5" } }, "sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA=="],
"@clack/prompts": ["@clack/prompts@1.1.0", "", { "dependencies": { "@clack/core": "1.1.0", "sisteransi": "^1.0.5" } }, "sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g=="],
"@ctrl/tinycolor": ["@ctrl/tinycolor@4.2.0", "", {}, "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A=="],
"@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
@@ -118,19 +123,15 @@
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
"@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="],
"@eslint/config-array": ["@eslint/config-array@0.23.3", "", { "dependencies": { "@eslint/object-schema": "^3.0.3", "debug": "^4.3.1", "minimatch": "^10.2.4" } }, "sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw=="],
"@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="],
"@eslint/config-helpers": ["@eslint/config-helpers@0.5.3", "", { "dependencies": { "@eslint/core": "^1.1.1" } }, "sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw=="],
"@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="],
"@eslint/core": ["@eslint/core@1.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ=="],
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.3", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ=="],
"@eslint/object-schema": ["@eslint/object-schema@3.0.3", "", {}, "sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ=="],
"@eslint/js": ["@eslint/js@9.39.3", "", {}, "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw=="],
"@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="],
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="],
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.6.1", "", { "dependencies": { "@eslint/core": "^1.1.1", "levn": "^0.4.1" } }, "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ=="],
"@expressive-code/core": ["@expressive-code/core@0.41.6", "", { "dependencies": { "@ctrl/tinycolor": "^4.0.4", "hast-util-select": "^6.0.2", "hast-util-to-html": "^9.0.1", "hast-util-to-text": "^4.0.1", "hastscript": "^9.0.0", "postcss": "^8.4.38", "postcss-nested": "^6.0.1", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1" } }, "sha512-FvJQP+hG0jWi/FLBSmvHInDqWR7jNANp9PUDjdMqSshHb0y7sxx3vHuoOr6SgXjWw+MGLqorZyPQ0aAlHEok6g=="],
@@ -292,17 +293,19 @@
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA=="],
"@shikijs/core": ["@shikijs/core@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA=="],
"@shikijs/core": ["@shikijs/core@4.0.2", "", { "dependencies": { "@shikijs/primitive": "4.0.2", "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw=="],
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-jdKhfgW9CRtj3Tor0L7+yPwdG3CgP7W+ZEqSsojrMzCjD1e0IxIbwUMDDpYlVBlC08TACg4puwFGkZfLS+56Tw=="],
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag=="],
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA=="],
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg=="],
"@shikijs/langs": ["@shikijs/langs@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0" } }, "sha512-x/42TfhWmp6H00T6uwVrdTJGKgNdFbrEdhaDwSR5fd5zhQ1Q46bHq9EO61SCEWJR0HY7z2HNDMaBZp8JRmKiIA=="],
"@shikijs/langs": ["@shikijs/langs@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2" } }, "sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg=="],
"@shikijs/themes": ["@shikijs/themes@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0" } }, "sha512-o+tlOKqsr6FE4+mYJG08tfCFDS+3CG20HbldXeVoyP+cYSUxDhrFf3GPjE60U55iOkkjbpY2uC3It/eeja35/g=="],
"@shikijs/primitive": ["@shikijs/primitive@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw=="],
"@shikijs/types": ["@shikijs/types@3.22.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg=="],
"@shikijs/themes": ["@shikijs/themes@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2" } }, "sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA=="],
"@shikijs/types": ["@shikijs/types@4.0.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg=="],
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
@@ -338,6 +341,8 @@
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
"@types/esrecurse": ["@types/esrecurse@4.3.1", "", {}, "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw=="],
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="],
@@ -362,19 +367,19 @@
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.56.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.57.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.56.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.56.1", "@typescript-eslint/types": "^8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.57.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.57.0", "@typescript-eslint/types": "^8.57.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1" } }, "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0" } }, "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.56.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.57.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.57.0", "", {}, "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.56.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.56.1", "@typescript-eslint/tsconfig-utils": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.57.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.57.0", "@typescript-eslint/tsconfig-utils": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg=="],
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
@@ -384,12 +389,6 @@
"ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
"ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
"ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
"arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
@@ -402,7 +401,7 @@
"astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="],
"astro": ["astro@5.18.0", "", { "dependencies": { "@astrojs/compiler": "^2.13.0", "@astrojs/internal-helpers": "0.7.5", "@astrojs/markdown-remark": "6.3.10", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^4.0.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "acorn": "^8.15.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.3.1", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.1.1", "cssesc": "^3.0.0", "debug": "^4.4.3", "deterministic-object-hash": "^2.0.2", "devalue": "^5.6.2", "diff": "^8.0.3", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.27.3", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.4.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "magic-string": "^0.30.21", "magicast": "^0.5.1", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.1", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.3", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.3", "shiki": "^3.21.0", "smol-toml": "^1.6.0", "svgo": "^4.0.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.7.3", "unist-util-visit": "^5.0.0", "unstorage": "^1.17.4", "vfile": "^6.0.3", "vite": "^6.4.1", "vitefu": "^1.1.1", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.3", "zod": "^3.25.76", "zod-to-json-schema": "^3.25.1", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "astro.js" } }, "sha512-CHiohwJIS4L0G6/IzE1Fx3dgWqXBCXus/od0eGUfxrZJD2um2pE7ehclMmgL/fXqbU7NfE1Ze2pq34h2QaA6iQ=="],
"astro": ["astro@6.0.4", "", { "dependencies": { "@astrojs/compiler": "^3.0.0", "@astrojs/internal-helpers": "0.8.0", "@astrojs/markdown-remark": "7.0.0", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.0.1", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^1.1.1", "devalue": "^5.6.3", "diff": "^8.0.3", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.27.3", "flattie": "^1.1.1", "fontace": "~0.4.1", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.3", "rehype": "^13.0.2", "semver": "^7.7.4", "shiki": "^4.0.0", "smol-toml": "^1.6.0", "svgo": "^4.0.0", "tinyclip": "^0.1.6", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unist-util-visit": "^5.1.0", "unstorage": "^1.17.4", "vfile": "^6.0.3", "vite": "^7.3.1", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "bin/astro.mjs" } }, "sha512-1piLJCPTL/x7AMO2cjVFSTFyRqKuC3W8sSEySCt1aJio+p/wGs5H3K+Xr/rE9ftKtknLUtjxCqCE7/0NsXfGpQ=="],
"astro-eslint-parser": ["astro-eslint-parser@1.3.0", "", { "dependencies": { "@astrojs/compiler": "^2.0.0", "@typescript-eslint/scope-manager": "^7.0.0 || ^8.0.0", "@typescript-eslint/types": "^7.0.0 || ^8.0.0", "astrojs-compiler-sync": "^1.0.0", "debug": "^4.3.4", "entities": "^6.0.0", "eslint-scope": "^8.0.1", "eslint-visitor-keys": "^4.0.0", "espree": "^10.0.0", "fast-glob": "^3.3.3", "is-glob": "^4.0.3", "semver": "^7.3.8" } }, "sha512-aOLc/aDR7lTWAHlytEefwn4Y6qs6uMr69DZvUx2A1AOAZsWhGB/paiRWPtVchh9wzMvLeqr+DkbENhVreVr9AQ=="],
@@ -414,9 +413,7 @@
"bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"base-64": ["base-64@1.0.0", "", {}, "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="],
"balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"bcp-47": ["bcp-47@2.1.0", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w=="],
@@ -424,20 +421,12 @@
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
"boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="],
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
"brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
"camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="],
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
"character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
@@ -450,23 +439,15 @@
"ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="],
"cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="],
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
"commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="],
"common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"common-ancestor-path": ["common-ancestor-path@2.0.0", "", {}, "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng=="],
"cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
@@ -502,8 +483,6 @@
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"deterministic-object-hash": ["deterministic-object-hash@2.0.2", "", { "dependencies": { "base-64": "^1.0.0" } }, "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ=="],
"devalue": ["devalue@5.6.3", "", {}, "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg=="],
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
@@ -524,13 +503,11 @@
"dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="],
"emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
"enhanced-resolve": ["enhanced-resolve@5.19.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg=="],
"entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
"es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
"es-module-lexer": ["es-module-lexer@2.0.0", "", {}, "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw=="],
"esast-util-from-estree": ["esast-util-from-estree@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "unist-util-position-from-estree": "^2.0.0" } }, "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ=="],
@@ -540,7 +517,7 @@
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"eslint": ["eslint@9.39.3", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.3", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg=="],
"eslint": ["eslint@10.0.3", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.3", "@eslint/config-helpers": "^0.5.2", "@eslint/core": "^1.1.1", "@eslint/plugin-kit": "^0.6.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.1.1", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ=="],
"eslint-compat-utils": ["eslint-compat-utils@0.6.5", "", { "dependencies": { "semver": "^7.5.4" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ=="],
@@ -570,7 +547,7 @@
"estree-util-visit": ["estree-util-visit@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/unist": "^3.0.0" } }, "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww=="],
"estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
"estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
@@ -590,6 +567,8 @@
"fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
"fault": ["fault@2.0.1", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ=="],
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
@@ -608,9 +587,9 @@
"fontkitten": ["fontkitten@1.0.2", "", { "dependencies": { "tiny-inflate": "^1.0.3" } }, "sha512-piJxbLnkD9Xcyi7dWJRnqszEURixe7CrF/efBfbffe2DPyabmuIuqraruY8cXTs19QoM8VJzx47BDRVNXETM7Q=="],
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="],
"get-east-asian-width": ["get-east-asian-width@1.5.0", "", {}, "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA=="],
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
@@ -622,8 +601,6 @@
"h3": ["h3@1.15.5", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.5", "defu": "^6.1.4", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"hast-util-embedded": ["hast-util-embedded@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-is-element": "^3.0.0" } }, "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA=="],
"hast-util-format": ["hast-util-format@1.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-embedded": "^3.0.0", "hast-util-minify-whitespace": "^1.0.0", "hast-util-phrasing": "^3.0.0", "hast-util-whitespace": "^3.0.0", "html-whitespace-sensitive-tag-names": "^3.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA=="],
@@ -676,10 +653,6 @@
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
"import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
"inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="],
@@ -696,8 +669,6 @@
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
"is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="],
@@ -724,8 +695,6 @@
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
"kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
"klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="],
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
@@ -756,8 +725,6 @@
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
"longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
"lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="],
@@ -778,6 +745,8 @@
"mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="],
"mdast-util-frontmatter": ["mdast-util-frontmatter@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "escape-string-regexp": "^5.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-extension-frontmatter": "^2.0.0" } }, "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA=="],
"mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="],
"mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="],
@@ -816,6 +785,8 @@
"micromark-extension-directive": ["micromark-extension-directive@3.0.2", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "parse-entities": "^4.0.0" } }, "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA=="],
"micromark-extension-frontmatter": ["micromark-extension-frontmatter@2.0.0", "", { "dependencies": { "fault": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg=="],
"micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="],
"micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="],
@@ -884,7 +855,7 @@
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"minimatch": ["minimatch@3.1.3", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA=="],
"minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
@@ -906,6 +877,8 @@
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
"obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="],
"ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
@@ -916,20 +889,18 @@
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
"p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="],
"p-limit": ["p-limit@7.3.0", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw=="],
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
"p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="],
"p-queue": ["p-queue@9.1.0", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^7.0.0" } }, "sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw=="],
"p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="],
"p-timeout": ["p-timeout@7.0.1", "", {}, "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg=="],
"package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="],
"pagefind": ["pagefind@1.4.0", "", { "optionalDependencies": { "@pagefind/darwin-arm64": "1.4.0", "@pagefind/darwin-x64": "1.4.0", "@pagefind/freebsd-x64": "1.4.0", "@pagefind/linux-arm64": "1.4.0", "@pagefind/linux-x64": "1.4.0", "@pagefind/windows-x64": "1.4.0" }, "bin": { "pagefind": "lib/runner/bin.cjs" } }, "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g=="],
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="],
"parse-latin": ["parse-latin@7.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ=="],
@@ -962,8 +933,6 @@
"prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
"prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="],
"property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
@@ -1002,8 +971,12 @@
"rehype-stringify": ["rehype-stringify@10.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-to-html": "^9.0.0", "unified": "^11.0.0" } }, "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA=="],
"remark": ["remark@15.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A=="],
"remark-directive": ["remark-directive@3.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-directive": "^3.0.0", "micromark-extension-directive": "^3.0.0", "unified": "^11.0.0" } }, "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A=="],
"remark-frontmatter": ["remark-frontmatter@5.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-frontmatter": "^2.0.0", "micromark-extension-frontmatter": "^2.0.0", "unified": "^11.0.0" } }, "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ=="],
"remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="],
"remark-mdx": ["remark-mdx@3.1.1", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg=="],
@@ -1016,8 +989,6 @@
"remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="],
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
"retext": ["retext@9.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "retext-latin": "^4.0.0", "retext-stringify": "^4.0.0", "unified": "^11.0.0" } }, "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA=="],
"retext-latin": ["retext-latin@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "parse-latin": "^7.0.0", "unified": "^11.0.0" } }, "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA=="],
@@ -1046,11 +1017,11 @@
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"shiki": ["shiki@3.22.0", "", { "dependencies": { "@shikijs/core": "3.22.0", "@shikijs/engine-javascript": "3.22.0", "@shikijs/engine-oniguruma": "3.22.0", "@shikijs/langs": "3.22.0", "@shikijs/themes": "3.22.0", "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-LBnhsoYEe0Eou4e1VgJACes+O6S6QC0w71fCSp5Oya79inkwkm15gQ1UF6VtQ8j/taMDh79hAB49WUk8ALQW3g=="],
"shiki": ["shiki@4.0.2", "", { "dependencies": { "@shikijs/core": "4.0.2", "@shikijs/engine-javascript": "4.0.2", "@shikijs/engine-oniguruma": "4.0.2", "@shikijs/langs": "4.0.2", "@shikijs/themes": "4.0.2", "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ=="],
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
"sitemap": ["sitemap@8.0.2", "", { "dependencies": { "@types/node": "^17.0.5", "@types/sax": "^1.2.1", "arg": "^5.0.0", "sax": "^1.4.1" }, "bin": { "sitemap": "dist/cli.js" } }, "sha512-LwktpJcyZDoa0IL6KT++lQ53pbSrx2c9ge41/SeLTyqy2XUNA6uR4+P9u5IVo5lPeL2arAcOKn1aZAxoYbCKlQ=="],
"sitemap": ["sitemap@9.0.1", "", { "dependencies": { "@types/node": "^24.9.2", "@types/sax": "^1.2.1", "arg": "^5.0.0", "sax": "^1.4.1" }, "bin": { "sitemap": "dist/esm/cli.js" } }, "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ=="],
"smol-toml": ["smol-toml@1.6.0", "", {}, "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw=="],
@@ -1060,24 +1031,18 @@
"space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
"starlight-versions": ["starlight-versions@0.8.1", "", { "dependencies": { "@pagefind/default-ui": "^1.3.0", "github-slugger": "^2.0.0", "mdast-util-mdx-jsx": "^3.2.0", "mdast-util-mdxjs-esm": "^2.0.1", "remark": "^15.0.1", "remark-directive": "^4.0.0", "remark-frontmatter": "^5.0.0", "remark-mdx": "^3.1.1", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3", "yaml": "^2.8.2" }, "peerDependencies": { "@astrojs/starlight": ">=0.38.0" } }, "sha512-b8fGFnYDI1vC1x/kFuEchr7zBd6hY/V9UUUFsyxGK/ZejKVpLNOFAblvzVDj8Rp1tqbLXAhszcft9K1LuLSHVQ=="],
"stream-replace-string": ["stream-replace-string@2.0.0", "", {}, "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w=="],
"string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
"stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
"strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
"style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="],
"style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="],
"suf-log": ["suf-log@2.5.3", "", { "dependencies": { "s.color": "0.0.15" } }, "sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow=="],
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"svgo": ["svgo@4.0.0", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.4.1" }, "bin": "./bin/svgo.js" }, "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw=="],
"synckit": ["synckit@0.11.12", "", { "dependencies": { "@pkgr/core": "^0.2.9" } }, "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ=="],
@@ -1088,6 +1053,8 @@
"tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
"tinyclip": ["tinyclip@0.1.12", "", {}, "sha512-Ae3OVUqifDw0wBriIBS7yVaW44Dp6eSHQcyq4Igc7eN2TJH/2YsicswaW+J/OuMvhpDPOKEgpAZCjkb4hpoyeA=="],
"tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
@@ -1106,8 +1073,6 @@
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="],
@@ -1116,6 +1081,8 @@
"uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
"unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
"unifont": ["unifont@0.7.4", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg=="],
@@ -1162,35 +1129,25 @@
"which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="],
"widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="],
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
"wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
"xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="],
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
"yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="],
"yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="],
"yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="],
"yocto-spinner": ["yocto-spinner@0.2.3", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ=="],
"yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="],
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
"zod-to-ts": ["zod-to-ts@1.2.0", "", { "peerDependencies": { "typescript": "^4.9.4 || ^5.0.2", "zod": "^3" } }, "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA=="],
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
"@expressive-code/plugin-shiki/shiki": ["shiki@3.22.0", "", { "dependencies": { "@shikijs/core": "3.22.0", "@shikijs/engine-javascript": "3.22.0", "@shikijs/engine-oniguruma": "3.22.0", "@shikijs/langs": "3.22.0", "@shikijs/themes": "3.22.0", "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-LBnhsoYEe0Eou4e1VgJACes+O6S6QC0w71fCSp5Oya79inkwkm15gQ1UF6VtQ8j/taMDh79hAB49WUk8ALQW3g=="],
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"@mdx-js/mdx/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
@@ -1204,30 +1161,38 @@
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
"@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
"ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"astro/@astrojs/compiler": ["@astrojs/compiler@3.0.0", "", {}, "sha512-MwAbDE5mawZ1SS+D8qWiHdprdME5Tlj2e0YjxnEICvcOpbSukNS7Sa7hA5PK+6RrmUr/t6Gi5YgrdZKjbO/WPQ=="],
"astro/vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
"astro-eslint-parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.56.0", "", { "dependencies": { "@typescript-eslint/types": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0" } }, "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w=="],
"astro-eslint-parser/@typescript-eslint/types": ["@typescript-eslint/types@8.56.0", "", {}, "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ=="],
"boxen/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
"csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="],
"dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"eslint/eslint-scope": ["eslint-scope@9.1.2", "", { "dependencies": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ=="],
"eslint/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
"eslint/espree": ["espree@11.2.0", "", { "dependencies": { "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^5.0.1" } }, "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw=="],
"eslint-plugin-astro/@typescript-eslint/types": ["@typescript-eslint/types@8.56.0", "", {}, "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ=="],
"estree-util-build-jsx/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
"mdast-util-frontmatter/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
@@ -1236,15 +1201,23 @@
"postcss-nested/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
"sitemap/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="],
"starlight-versions/remark-directive": ["remark-directive@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-directive": "^3.0.0", "micromark-extension-directive": "^4.0.0", "unified": "^11.0.0" } }, "sha512-7sxn4RfF1o3izevPV1DheyGDD6X4c9hrGpfdUpm7uC++dqrnJxIZVkk7CoKqcLm0VUMAuOol7Mno3m6g8cfMuA=="],
"vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
"@expressive-code/plugin-shiki/shiki/@shikijs/core": ["@shikijs/core@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
"@expressive-code/plugin-shiki/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-jdKhfgW9CRtj3Tor0L7+yPwdG3CgP7W+ZEqSsojrMzCjD1e0IxIbwUMDDpYlVBlC08TACg4puwFGkZfLS+56Tw=="],
"ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"@expressive-code/plugin-shiki/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA=="],
"ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"@expressive-code/plugin-shiki/shiki/@shikijs/langs": ["@shikijs/langs@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0" } }, "sha512-x/42TfhWmp6H00T6uwVrdTJGKgNdFbrEdhaDwSR5fd5zhQ1Q46bHq9EO61SCEWJR0HY7z2HNDMaBZp8JRmKiIA=="],
"@expressive-code/plugin-shiki/shiki/@shikijs/themes": ["@shikijs/themes@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0" } }, "sha512-o+tlOKqsr6FE4+mYJG08tfCFDS+3CG20HbldXeVoyP+cYSUxDhrFf3GPjE60U55iOkkjbpY2uC3It/eeja35/g=="],
"@expressive-code/plugin-shiki/shiki/@shikijs/types": ["@shikijs/types@3.22.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg=="],
"astro-eslint-parser/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.0", "", { "dependencies": { "@typescript-eslint/types": "8.56.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg=="],
@@ -1252,6 +1225,8 @@
"p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
"starlight-versions/remark-directive/micromark-extension-directive": ["micromark-extension-directive@4.0.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "parse-entities": "^4.0.0" } }, "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg=="],
"vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
"vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
@@ -1304,10 +1279,6 @@
"vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"astro-eslint-parser/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
}
}

View File

@@ -10,20 +10,21 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/starlight": "^0.37.6",
"@astrojs/starlight-tailwind": "^4.0.2",
"@astrojs/starlight": "^0.38.1",
"@astrojs/starlight-tailwind": "^5.0.0",
"@fontsource/ibm-plex-mono": "^5.2.7",
"@fontsource/ibm-plex-sans": "^5.2.8",
"@fontsource/ibm-plex-serif": "^5.2.7",
"@tailwindcss/vite": "^4.2.1",
"astro": "^5.18.0",
"astro": "^6.0.4",
"sharp": "^0.34.5",
"starlight-versions": "^0.8.1",
"tailwindcss": "^4.2.1"
},
"devDependencies": {
"@typescript-eslint/parser": "^8.56.1",
"@typescript-eslint/parser": "^8.57.0",
"astro-eslint-parser": "^1.3.0",
"eslint": "^9.39.3",
"eslint": "^10.0.3",
"eslint-plugin-astro": "^1.6.0",
"prettier": "^3.8.1",
"prettier-plugin-astro": "^0.14.1",

3
src/assets/1.0/grlx.webp Normal file
View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5de242e9332bf9e47d1331e19604fc13e8d2eb9f32d8a56d53dd306831ee3c03
size 30508

View File

@@ -29,9 +29,12 @@ import Posthog from './Posthog.astro'
</a>
<p>&copy; 2026 ADAtomic, Inc. All rights reserved.</p>
</div>
<div class="flex justify-end items-end">
<a href="https://shop.grlx.dev" class="text-accent-600 dark:text-accent-200"><p >shop.grlx.dev</p></a>
<div class="flex items-end justify-end">
<a
href="https://shop.grlx.dev"
class="text-accent-600 dark:text-accent-200"><p>shop.grlx.dev</p></a
>
</div>
</div>
<Posthog/>
<Posthog />
</footer>

View File

@@ -1,14 +1,10 @@
---
import { Image } from 'astro:assets'
import darkArch from "../assets/grlx-arch-dark.webp"
import darkArch from '../assets/grlx-arch-dark.webp'
import lightArch from '../assets/grlx-arch-light.webp'
---
<Image
class="dark:hidden"
src={lightArch}
alt="ADAtomic logo"
/>
<Image class="dark:hidden" src={lightArch} alt="ADAtomic logo" />
<Image
class="hidden dark:inline"
src={darkArch}

View File

@@ -1,8 +1,60 @@
---
---
<script>
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
posthog.init('phc_WaZmfSjKT84EymwV3W6BG9yGH9y1kUuhnJfXd3ehepU',{api_host:'https://us.i.posthog.com', person_profiles: 'identified_only' // or 'always' to create profiles for anonymous users as well
})
!(function (t, e) {
var o, n, p, r
e.__SV ||
((window.posthog = e),
(e._i = []),
(e.init = function (i, s, a) {
function g(t, e) {
var o = e.split('.')
;(2 == o.length && ((t = t[o[0]]), (e = o[1])),
(t[e] = function () {
t.push([e].concat(Array.prototype.slice.call(arguments, 0)))
}))
}
;(((p = t.createElement('script')).type = 'text/javascript'),
(p.async = !0),
(p.src =
s.api_host.replace('.i.posthog.com', '-assets.i.posthog.com') +
'/static/array.js'),
(r = t.getElementsByTagName('script')[0]).parentNode.insertBefore(
p,
r,
))
var u = e
for (
void 0 !== a ? (u = e[a] = []) : (a = 'posthog'),
u.people = u.people || [],
u.toString = function (t) {
var e = 'posthog'
return (
'posthog' !== a && (e += '.' + a),
t || (e += ' (stub)'),
e
)
},
u.people.toString = function () {
return u.toString(1) + '.people (stub)'
},
o =
'init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug'.split(
' ',
),
n = 0;
n < o.length;
n++
)
g(u, o[n])
e._i.push([i, s, a])
}),
(e.__SV = 1))
})(document, window.posthog || [])
posthog.init('phc_WaZmfSjKT84EymwV3W6BG9yGH9y1kUuhnJfXd3ehepU', {
api_host: 'https://us.i.posthog.com',
person_profiles: 'identified_only', // or 'always' to create profiles for anonymous users as well
})
</script>

9
src/content.config.ts Normal file
View File

@@ -0,0 +1,9 @@
import { docsLoader } from '@astrojs/starlight/loaders'
import { docsSchema } from '@astrojs/starlight/schema'
import { defineCollection } from 'astro:content'
import { docsVersionsLoader } from 'starlight-versions/loader'
export const collections = {
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
versions: defineCollection({ loader: docsVersionsLoader() }),
}

View File

@@ -1,7 +0,0 @@
import { defineCollection } from 'astro:content'
import { docsSchema, i18nSchema } from '@astrojs/starlight/schema'
export const collections = {
docs: defineCollection({ schema: docsSchema() }),
i18n: defineCollection({ type: 'data', schema: i18nSchema() }),
}

View File

@@ -0,0 +1,29 @@
---
title: Overview
description: A brief description of the grlx architecture
slug: 1.0/architecture/overview
---
import ArchitectureImage from '../../../../components/ArchitectureImage.astro'
At a high level the grlx architecture looks like the following:
<ArchitectureImage />
We can boil down the `grlx` into three different parts: the farmer, the sprout, and the CLI.
## Farmer
The farmer is your management server running the `grlx-farmer` binary. The binary can run as a systemd service or be hosted in a container. This has analogs in other similar configuration platforms such as a Salt Master in SaltStack or a Chef Server in Chef. grlx utilizes the farmer for things like authentication, job processing, and [ingredients](/1.0/architecture/overview#ingredients). Additionally, the farmer is responsible for holding your [recipes](/1.0/architecture/overview#recipes).
## Sprout
The sprout is a managed node running the `grlx-sprout` binary. Sprouts receive commands from the farmer over the NATS message bus. These can be immediate shell commands, or actions that are performed by Ingredients defined in a recipe (e.g. ensure that a file exists, a service is started). Sprouts are similar to a Salt Minion in SaltStack or a Chef Client in Chef. Jobs are received from the farmer over the NATS message bus. Sprouts will then execute the job and report back to the farmer the results of the job.
## CLI
The CLI is a tool used to interact with the farmer and manage sprouts. The CLI is used to accept keys from sprouts and `cook` (run) recipes that are hosted on the farmer. The CLI also contains tools to tail the traffic over the NATS bus, test sprout connections, and run arbitrary commands on sprouts.
## Recipes
Recipes are the core of grlx and are responsible for handling the configuration of your sprouts. Recipes are written with the `.grlx` extension and are a combination of YAML and Go templates. Recipes are stored on the farmer and are executed by the sprouts. Recipes are responsible for defining the [ingredients](/1.0/architecture/overview#ingredients) and in what order they are executed. For more information see the [recipes](/1.0/recipes/overview) section.

View File

@@ -0,0 +1,266 @@
---
title: Getting Started
description: Getting Started with grlx
slug: 1.0/getting-started
---
import { Tabs, TabItem } from '@astrojs/starlight/components'
Want to get up and running as quickly as possible to see what all the fuss is about?
Use our bootstrap scripts! Follow our Quick Start to get started!
## Quick Start
### 1. Download and initialize the CLI
Install the CLI on your development machine with the correct command for your architecture:
<Tabs>
<TabItem label="Linux">
<Tabs>
<TabItem label="amd64">
```bash
curl -L https://releases.grlx.dev/linux/amd64/latest/grlx > grlx && chmod +x grlx
./grlx init
```
</TabItem>
<TabItem label="i386">
```bash
curl -L https://releases.grlx.dev/linux/386/latest/grlx > grlx && chmod +x grlx
./grlx init
```
</TabItem>
<TabItem label="arm">
```bash
curl -L https://releases.grlx.dev/linux/arm/latest/grlx > grlx && chmod +x grlx
./grlx init
```
</TabItem>
<TabItem label="arm64">
```bash
curl -L https://releases.grlx.dev/linux/arm64/latest/grlx > grlx && chmod +x grlx
./grlx init
```
</TabItem>
</Tabs>
</TabItem>
<TabItem label="macOS">
<Tabs>
<TabItem label="x86_64">
```bash
curl -L https://releases.grlx.dev/darwin/amd64/latest/grlx > grlx && chmod +x grlx
./grlx init
```
</TabItem>
<TabItem label="arm64">
```bash
curl -L https://releases.grlx.dev/darwin/arm64/latest/grlx > grlx && chmod +x grlx
./grlx init
```
</TabItem>
</Tabs>
</TabItem>
</Tabs>
You'll be asked some questions, such as which interface the farmer is listening on, and which ports to use for communication.
Set the interface to the domain name or IP address of the farmer.
If you plan to install the farmer on k3s (see below), then use the IP address of your k3s server, API port 30405, and Bus port 30406. If you plan to locally run the Docker Compose file below, then all of the defaults are good (localhost and default ports).
Once configured, the CLI prints out your administrator public key, which you'll need for the next step!
It's recommended you now add `grlx` somewhere in your `$PATH`.
***
### 2. Install the farmer on the management server
You will then need to install the farmer on the management server. The farmer is the central server that manages the fleet.
<Tabs>
<TabItem label="systemd (Recommended)">
```bash
# You need to perform this task as root
curl -L https://bootstrap.grlx.dev/latest/farmer | bash
```
You'll be asked several questions about the interface to listen on, which ports to use, etc.
For the quick start, it's recommended to use the default ports (TCP 5405-5406).
You'll be prompted for an admin public key, which you should have gotten from the prior step, and a certificate host name(s).
Make sure the certificate host name matches the external-facing interface (a domain or IP address) as it will be used for TLS validation!
</TabItem>
<TabItem label="k3s">
Copy the resource YAML below. Replace `PASTE_YOUR_PUBLIC_KEY_HERE` with your admin public key, and `PASTE_YOUR_FARMERINTERFACE_HERE` with the domain name or IP address of the farmer.
Deploy the farmer, e.g. `kubectl apply -f grlx-farmer.yaml`. This will deploy a single farmer to which you can add many external sprouts.
```yaml
apiVersion: v1
kind: Service
metadata:
name: grlx-farmer-np
spec:
type: NodePort
ports:
- name: api
port: 5405
nodePort: 30405
- name: bus
port: 5406
nodePort: 30406
selector:
statefulset.kubernetes.io/pod-name: grlx-farmer-0
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: grlx-farmer
spec:
selector:
matchLabels:
app: grlx
role: farmer
serviceName: grlx-farmer
replicas: 1
template:
metadata:
labels:
app: grlx
role: farmer
spec:
containers:
- name: main
image: grlx/farmer:latest
env:
- name: ADMIN_PUBKEYS
value: "PASTE_YOUR_PUBLIC_KEY_HERE"
- name: CERT_HOSTS
value: "localhost,PASTE_YOUR_FARMERINTERFACE_HERE"
- name: FARMERINTERFACE
value: "0.0.0.0" # do not modify
- name: FARMERORGANIZATION
value: "Your Organization" # change to your org name
ports:
- name: api
containerPort: 5405
- name: bus
containerPort: 5406
volumeMounts:
- name: cache
mountPath: /var/cache/grlx
- name: config
mountPath: /etc/grlx
- name: data
mountPath: /srv/grlx
volumes:
- name: cache
emptyDir: {}
- name: config
hostPath:
path: /etc/grlx
type: DirectoryOrCreate
- name: data
hostPath:
path: /srv/grlx
type: DirectoryOrCreate
```
</TabItem>
<TabItem label="Docker Compose">
Copy the Docker Compose YAML below. Replace `PASTE_YOUR_PUBLIC_KEY_HERE` with your admin public key, then `docker compose up` to fire up an all-in-one farmer/sprout environment.
```yaml
version: "3"
services:
farmer:
container_name: farmer
image: grlx/farmer:latest
hostname: farmer
environment:
- ADMIN_PUBKEYS=PASTE_YOUR_PUBLIC_KEY_HERE
- CERT_HOSTS=localhost,farmer
- FARMERINTERFACE=0.0.0.0
volumes:
- ./local/farmer:/etc/grlx
# This is where the farmer whill store its recipes
- ./local/data:/srv/grlx
ports:
- "5405:5405"
- "5406:5406"
sprout: # for testing grlx features only, not for production
container_name: sprout
image: grlx/sprout:latest
hostname: sprout
volumes:
- ./local/sprout:/etc/grlx
environment:
- FARMERINTERFACE=farmer
```
</TabItem>
</Tabs>
***
### 3. Install the sprout on every node you want to manage
The sprout is the agent that runs on every node you want to manage. It's responsible for executing commands and reporting back to the farmer.
```bash
# Be sure to change FARMERINTERFACE to the domain or IP address of the farmer.
# FARMERBUSPORT and FARMERAPIPORT variables are available in case you need
# to use different ports (see k3s note below).
curl -L https://bootstrap.grlx.dev/latest/sprout | FARMERINTERFACE=localhost bash
```
NOTE: If you deployed the k3s farmer above, then use:
```bash
# Be sure to change FARMERINTERFACE to the domain or IP address of the farmer.
curl -L https://bootstrap.grlx.dev/latest/sprout | FARMERBUSPORT=30406 FARMERAPIPORT=30405 FARMERINTERFACE=localhost bash
```
Once the sprout is up and running, return to the CLI.
***
### 4. Accept the TLS cert and the sprout keys
Next, you must accept each sprout's public key and the farmer's TLS certificate. This is done using the `keys` command. Furthermore, using `grlx version`, shows you that you are able to connect to your farmer.
```bash
# You will be asked to download and trust the TLS certificate for
# the farmer, choose Y.
grlx version
grlx keys accept -A
```
***
### 5. Now you are ready to `cook`!
```bash
grlx -T \* test ping
grlx -T \* cmd run whoami
grlx -T \* cmd run --out json -- uname -a
```
***
#### Uninstalling
If you ever need to uninstall the `grlx-farmer`, you can do so with the following commands:
```bash
curl -L https://bootstrap.grlx.dev/latest/farmer | UNINSTALL=true bash
```
Or the `grlx-sprout`:
```bash
curl -L https://bootstrap.grlx.dev/latest/sprout | UNINSTALL=true bash
```

View File

@@ -0,0 +1,61 @@
---
title: Glossary
description: A glossary of terms commonly used in grlx
slug: 1.0/glossary
---
## Farmer (grlx-farmer)
The farmer is your management server running the `grlx-farmer` binary. The binary can run as a systemd service or be hosted in a container. This has analogs in other similar configuration platforms. In SaltStack, this is your Salt Master, or in Chef, your Chef Server. grlx utilizes the farmer for things like authentication, job processing, and [ingredients](/1.0/architecture/overview#ingredients).
## Sprout (grlx-sprout)
The sprout is a managed node running the `grlx-sprout` binary. Sprouts receive commands from the farmer over the NATS message bus. These can be immediate shell commands, or actions that are performed by Ingredients defined in a recipe (e.g. ensure that a file exists). Sprouts are similar to a Salt Minion in SaltStack or a Chef Client in Chef.
## CLI (grlx CLI)
The CLI is a tool used to interact with the farmer and manage sprouts. The CLI is used to accept keys from sprouts and `cook` (run) recipes that are hosted on the farmer. The CLI also contains tools to tail the traffic over the NAT bus, test sprout connections, and run arbitrary commands on sprouts.
## Authentication
All communication is encrypted using self-signed TLS certificates and [NATS.io NKey encryption](https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth). Certificates are pinned to the clients on first connection as an extra security precaution. Furthermore, every CLI must accept a working certificate for any interactions with the farmer. NKeys provides a NATS native method for handling asymmetric encryption of messages between all parts of the system.
## Command Execution
The CLI has the ability to dispatch arbitrary shell commands to sprouts using `grlx cmd run`. Jobs are evaluated and dispatched to sprouts and their outputs are aggregated to the output of the CLI. Furthermore, this can be used inside of recipes for more structured deployments.
## Farmer Hooks (hooks)
Hooks provide a way to fire off webhooks when recipe deployment (cook) starts, fails or finishes. Additionally, you can configure a hook to fire when a new sprout is added to the farm, or when a sprouts status has changed. A “hook” ingredient is also provided (not shown) that can be fired from the sprout during steps of a recipe.
## Ingredients
[Ingredients](/1.0/ingredients/overview) are how we build configurations with grlx. They can be thought of as the building blocks for completing various file, service, or management operations. Ingredients use a provider interface to allow for simple extensibility via the [Go plugin system](https://pkg.go.dev/plugin).
## Template Engine
Templating is achieved with [Gos builtin templating engine](https://pkg.go.dev/text/template). This provides a Jinja-like templating system that hooks into the Farmer and Sprout properties to customize recipes to meet the dynamic needs of your needs.
## Job Enqueuing and Processing
You initiate jobs from the CLI by cooking recipes. Processing is continued on the farmer, and sprouts stream results to the CLI in real-time over the message bus.
## Properties (Farmer, Sprout and Fetched Sprout Props)
Both farmers and sprouts have properties for use in recipes. These properties are used in recipe templates to specify conditional execution of parts of the recipe. One example of this might be the specific OS or architecture of a sprout to ensure that the correct package gets installed on the correct machine.
## API
Each farmer has a REST API used for querying information about jobs or other information the farmer has access to. API tokens can be generated by the `grlx auth token`.
## Local API
The Local API is hosted on the grlx CLI to run a web frontend application for grlx. The local API provides a simple yet extensible interface for interacting with the CLI without the need for a terminal.
## Message Bus
An embedded [NATS.io server](https://nats.io/) is built into the farmer to handle connection between the sprouts, farmer, and the CLI. The bus uses NKeys to handle secure message passing between sprouts, the farmer, and CLIs. The message bus is responsible for handling communication between the CLI and the farmer as well as ensuring that jobs get dispatched to sprouts.
## Managed Node
The managed node is where the `grlx-sprout` daemon runs. It is the physical device where a sprout will deploy files, manage services, or run commands.

View File

@@ -0,0 +1,45 @@
---
title: grlx
description: Effective Fleet Configuration Management
template: splash
hero:
tagline: Effective Fleet Configuration Management
image:
file: ../../../assets/1.0/grlx.webp
actions:
- text: Get Started
link: /1.0/getting-started/
icon: right-arrow
variant: primary
- text: Check out our GitHub
link: https://github.com/gogrlx/grlx
icon: github
slug: "1.0"
---
import { Card, CardGrid } from '@astrojs/starlight/components'
## Next steps
<CardGrid stagger>
<Card title="Install grlx with our Getting Started" icon="laptop">
Check out our [Getting Started](/1.0/getting-started) started page on how to
install grlx
</Card>
<Card title="Learn about ingredient components" icon="setting">
Check out our [ingredients](/1.0/ingredients/overview) to learn more about how
to configure your fleet
</Card>
<Card title="Get more info" icon="open-book" class="open-book">
Check out the [Glossary](/1.0/glossary) for more info on grlx concepts
</Card>
<div class="discord-bg">
<Card title="Join our Discord" icon="discord" class="discord">
Join our [Discord](https://discord.gg/RNsZ3KWjXm) for learning more about
grlx
</Card>
</div>
</CardGrid>

View File

@@ -0,0 +1,34 @@
---
title: grlx.ingredients.cmd
description: cmd
slug: 1.0/ingredients/cmd
---
The cmd ingredient allows you to run arbitrary shell commands against sprouts.
## **cmd.run**
Runs shell commands against a sprout
#### Parameters
| parameter | type | required | description |
| --------- | ------ | -------- | ---------------------------------------------------------------------------------------- |
| _name_ | string | true | the command to run |
| _runas_ | string | false | user who will run this command (defaults to root) |
| _path_ | string | false | the path to the binary, this is prepended to the name |
| _cwd_ | string | false | the directory to run this command |
| _env_ | list | false | environment variables to set should be specified like key1=value1 |
| _timeout_ | string | false | set a timeout for the command using [Go Duration](https://pkg.go.dev/time#ParseDuration) |
```yaml
cmd.run:
- name: go version
- runas: super-cool-user
- path: /usr/local/bin
- cwd: /tmp
- env:
- GOOS=linux
- GOARCH=arm64
- timeout: 10s
```

View File

@@ -0,0 +1,43 @@
---
title: grlx.ingredients.file.providers
description: grlx built-in file providers
slug: 1.0/ingredients/file-providers
---
`grlx` has a concept of file providers for different ways that you might obtain a file to be added to a given sprout. This uses a provider interface to keep this extensible and provide a standard way to use different file types. The `go` interface for File Providers looks like the following:
```go
type FileProvider interface {
Download(context.Context) error
Properties() (map[string]interface{}, error)
Parse(id, source, destination, hash string, properties map[string]interface{}) (FileProvider, error)
Protocols() []string
Verify(context.Context) (bool, error)
}
```
By default, `grlx` has two built-in providers: local and HTTP.
## Local
The local provider would be how you would use a file from your host system.
#### Example
```yaml
file.cached:
- source: go1.21.3.src.tar.gz
- hash: sha256=186f2b6f8c8b704e696821b09ab2041a5c1ee13dcbc3156a13adcf75931ee488
```
## HTTP
The HTTP provider allows you to download files via HTTP for use. The [example](/1.0/ingredients/file-providers/#example) below is using the HTTP provider to download Go from the Internet. This file then gets cached to the sprout.
#### Example
```yaml
file.cached:
- source: https://go.dev/dl/go1.21.3.src.tar.gz
- hash: sha256=186f2b6f8c8b704e696821b09ab2041a5c1ee13dcbc3156a13adcf75931ee488
```

View File

@@ -0,0 +1,298 @@
---
title: grlx.ingredients.file
description: file
slug: 1.0/ingredients/file
---
import { Badge } from '@astrojs/starlight/components';
The file ingredient handles all file operations on various [file providers](/1.0/ingredients/file-providers) (such as local files, HTTP, etc.)
## **file.absent**
Deletes a file or directory
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| *name* | string | yes | the name/path of the file to delete
#### Example
```yaml
file.absent:
name: ~/.config/systemd/user/backup.service
```
## **file.append**
Appends content to a file. Only appends the content if it doesn't exist.
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| *name* | string | yes| the name/path of the file to append to
| *text* | string | no | the text to append to a file
| *makedirs* | bool | no | create parent directories if they do not exist
| *source* | string | no | append lines from a file sourced from this path/URL
| *source\_hash* | string | no | hash to verify the file specified by source
| *template* <Badge text="experimental" />| bool | no | treat this file as a template and render it before placing
| *sources* | string/list | no | appends all content specified in list, checked in order
| *source\_hashes* | string/list | no | corresponding hashes for sources
#### Example
```yaml
file.append:
- name: /etc/profile
- text: |
export PATH=$PATH:/usr/local/go/bin
```
## **file.cached**
Validates if a file is cached in the sprout's cache. If it isn't the file will be cached.
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| *name* | string | yes | the path describing where to save the cached file
| *source* | string | yes | a file source (such as HTTP, file, etc.) to reference
| *hash* | string | conditional (required if `skip_verify` is false) | a valid hash of the `source` file
| *skip\_verify* | boolean | no | whether to skip hash validation, false by default
#### Example
```yaml
file.cached:
- name: /tmp/cachedfile
- source: https://go.dev/dl/go1.21.3.src.tar.gz
- hash: sha256=186f2b6f8c8b704e696821b09ab2041a5c1ee13dcbc3156a13adcf75931ee488
```
## **file.contains**
Checks if a file contains a given selection. If multiple sources are provided, all must be satisfied.
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| *name* | string | yes | the name/path of the file to check
| *text* | string | no |the item to search for
| *source* | string | no | a file source (such as HTTP, file, etc.) to reference
| *source\_hash* | string | conditional (required if `source` provided) | a hash for a given source
| *sources* | list | no | a list of sources to check against
| *source\_hashes* | list | conditional (required if `skip_verify` is false) | a list of source hashes
| *skip\_verify* | boolean | no | whether to skip hash validation, false by default
| *template* <Badge text="experimental" />| bool | no | treat this file as a template and render it before placing it
## **file.content**
Copies content into a given file
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| *name* | string | yes | Represents the name of a file or directory
| *makedirs* | bool | no (default: `false`) | Determines whether directories should be created if they don't exist
| *source* | string | no | Represents a source file's path or name
| *source\_hash* | string | conditional (required if `source` is provided and `skip_verify` is `false`) | Represents the hash of a source file, used for verification
| *text* | string/list | no | Represents the content of a file. Can be a single string or a list of items
| *sources* | list | no | Represents multiple source files
| *template* <Badge text="experimental" />| bool | no | treat this file as a template and render it before placing it
| *skip\_verify* | boolean | no | whether to skip hash validation, false by default
| *source\_hashes* | list | conditional (required if `sources` is provided and `skip_verify` is `false`) | Represents the hashes for the multiple source files mentioned in `sources`
#### Example
```yaml
file.content:
- name: /srv/nginx/nginx.conf
- makdirs: true
- text: |
server {
listen 8080;
root /data/up1;
location / {
}
}
```
## **file.directory**
Handles many directory operations. Ensures that a directory exists with the given permissions.
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| *name* | string | yes | the name/path of the directory
| *makedirs* | bool | no |option to make directory if it doesn't exist, defaults to true
| *user* | string | no |the user who will own the directory
| *group* | string | no |the group who will own the directory
| *dir\_mode* | string | no |the directory mode
| *file\_mode* | string | no |the file mode to set
| *recurse* | bool | no |whether to recurse the directories and apply permissions
#### Example
```yaml
file.directory:
- name: /tmp/item
- makdirs: false
- user: grlx
- group: grlx
- dir_mode: 755
- recurse: false
```
## **file.exists**
Checks if a file exists.
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| *name* | string | yes | the name/path of the file
#### Example
```yaml
file.exists:
- name: /tmp/exists
```
## **file.managed**
Manage many properties of a file concurrently, safely
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| *name* | string | yes | Represents the name of a file or directory
| *source* | string | no | Represents a source file's path or name
| *source\_hash* | string | conditional (required if `skip_verify` is false) | Represents the hash of a source file, used for verification
| *user* | string | no |the user who will own the directory
| *group* | string | no |the group who will own the directory
| *mode* | string | no |the file's mode
| *template* <Badge text="experimental" />| bool | no | treat this file as a template and render it before placing it
| *makedirs* | bool | no (default: `false`) | Determines whether directories should be created if they don't exist
| *dir\_mode* | string | no |the directory mode
| *text* | string/list | no | Represents the content of a file. Can be a single string or a list of items
| *sources* | list | no | Represents multiple source files
| *skip\_verify* | boolean | no | whether to skip hash validation, false by default
| *source\_hashes* | list | conditional (required if `sources` is provided) | Represents the hashes for the multiple source files mentioned in `sources`
#### Example
```yaml
file.managed:
- name: /srv/nginx/nginx.conf
- makdirs: true
- user: pi
- group: wheel
- text: |
server {
listen 8080;
root /data/up1;
location / {
}
}
```
## **file.missing**
Checks if a file is missing.
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| *name* | string | yes | the name/path of the file
#### Example
```yaml
file.missing:
- name: /tmp/missing
```
## **file.prepend**
Prepends content to a file. Only prepends the content if it doesn't exist.
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| *name* | string | yes| the name/path of the file to prepend to
| *text* | string | no | the text to prepend to a file
| *makedirs* | bool | no | create parent directories if they do not exist
| *source* | string | no | prepend lines from a file sourced from this path/URL
| *source\_hash* | string | conditional (required if `source` is provided and `skip_verify` is `false`) | hash to verify the file specified by source
| *skip\_verify* | boolean | no | whether to skip hash validation, false by default
| *template* <Badge text="experimental" />| bool | no | treat this file as a template and render it before placing it
| *sources* | string/list | no | list source, but in list format
| *source\_hashes* | string/list | no | corresponding hashes for sources
#### Example
```yaml
file.prepend:
- name: /etc/profile
- text: |
export PATH=$PATH:/usr/local/go/bin
```
## **file.symlink**
Creates a symlink at `name` that points to `target`
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| *name* | string | yes | the name/path of the file
| *target* | string | yes | the target path to link to
| *makedirs* | string | no | make parent directories if missing
| *user* | string | no | the user who should own the symlink
| *group* | string | no | the group who should own the symlink
| *mode* | string | no | the desired filemode of the symlink
#### Example
```yaml
file.symlink:
- name: ~/localbash
- target: /usr/bin/bash
- user: pi
- group: wheel
- mode: "655"
```
## **file.touch**
Performs a `touch` on a file
| parameter | type | required | description |
|-----------|------|----------|-------------|
| *name* | string | yes | the name/path of the file
| *atime* | time (RFC339) | no | the access time to set the file to, set to the current time by default
| *mtime* | time (RFC339) | no | the modified time to set the file to, sets to the current time by default
| *makedirs* | bool | yes | whether or not to create any provided parent directories, false by default
```yaml
file.touch:
- name: /tmp/parent/new-file
- atime: Mon, 06 Nov 2023 00:00:00 +0000
- mtime: Mon, 06 Nov 2023 00:00:00 +0000
- makedirs: true
```

View File

@@ -0,0 +1,54 @@
---
title: grlx.ingredients.group
description: An overview of grlx.ingredients.group
slug: 1.0/ingredients/groups
---
The group ingredient handles group operations on sprouts.
## **group.absent**
Removes a group
#### Parameters
| parameter | type | required | description |
| --------- | ------ | -------- | ------------------------------- |
| _name_ | string | no | The name of the group to remove |
```yaml
group.absent:
- name: sproutgroup
```
## **group.exists**
Validates if a group exists
#### Parameters
| parameter | type | required | description |
| --------- | ------ | -------- | ----------------------- |
| _name_ | string | yes | The group name to check |
```yaml
group.exists:
- name: sproutgroup
```
## **group.present**
Creates a group if it does not exist
#### Parameters
| parameter | type | required | description |
| --------- | ------ | -------- | ------------------------------- |
| _name_ | string | yes | The name of the group to create |
| _gid_ | string | no | The GID of the group to create |
```yaml
group.present:
- name: sproutgroup
- gid: '1107'
```

View File

@@ -0,0 +1,13 @@
---
title: Overview
description: The foundation of grlx
sidebar:
order: 1
slug: 1.0/ingredients/overview
---
Recipe ingredients are how we build configurations with `grlx`. They can be thought of as the building blocks for completing various file, service, or management operations. Recipes rely on dependency injection via a provider system to provide extensible backends for various tasks. This also allows users to develop [go plugins](https://pkg.go.dev/plugin) to extend ingredient providers.
:::note
The [file ingredient](/1.0/ingredients/file) already supports a few different file providers. Check out the list of file providers [here](/1.0/ingredients/file-providers).
:::

View File

@@ -0,0 +1,71 @@
---
title: grlx.ingredients.service.providers
description: grlx built-in service providers
slug: 1.0/ingredients/service-providers
---
grlx has a concept of service providers for different ways to interact with a sprout's various services. This uses a provider interface to keep this extensible and provide a standard way to interact with different service managers. The Go interface for Service Providers looks like the following:
```go
type ServiceProvider interface {
Properties() (map[string]interface{}, error)
Parse(id, method string, properties map[string]interface{}) (ServiceProvider, error)
Start(context.Context) error
Stop(context.Context) error
Status(context.Context) (string, error)
Enable(context.Context) error
Disable(context.Context) error
IsEnabled(context.Context) (bool, error)
IsRunning(context.Context) (bool, error)
Restart(context.Context) error
Mask(context.Context) error
Unmask(context.Context) error
IsMasked(context.Context) (bool, error)
InitName() string
IsInit() bool
}
```
grlx automatically detects which init system is running on each sprout and selects the appropriate provider. The following providers are built in:
## systemd
The `systemd` provider manages services on Linux systems using systemd. This is the most common provider and is auto-detected on systems where `systemctl` is available.
#### Example
```yaml
service.enabled:
- name: cronie.service
- userMode: false
```
## rc.d (BSD)
The `rc.d` provider manages services on BSD systems (FreeBSD, NetBSD, OpenBSD, DragonFlyBSD) using the native rc.d init system. It is auto-detected on systems where `/etc/rc.d` or `/usr/local/etc/rc.d` is present.
The rc.d provider maps grlx service operations to the corresponding `service(8)` and `rc.conf` commands:
- **Start/Stop/Restart** — `service <name> start|stop|restart`
- **Enable/Disable** — adds or removes `<name>_enable="YES"` in `/etc/rc.conf`
- **Status** — `service <name> status`
#### Example
```yaml
service.running:
- name: sshd
```
:::note
The `userMode` parameter is not applicable to rc.d services and is ignored if set.
:::
:::note
The `mask`/`unmask` operations are supported on rc.d but use `rc.conf` variables to prevent service startup, since BSD rc.d does not have a native mask concept like systemd.
:::

View File

@@ -0,0 +1,126 @@
---
title: grlx.ingredients.service
description: service
slug: 1.0/ingredients/service
---
The `service` ingredient handles the management of system services. grlx uses a provider interface, so the same recipe syntax works across different init systems. See [service providers](/1.0/ingredients/service-providers) for supported backends.
## **service.masked**
Checks if a service is masked and masks it if it is not
#### Parameters
| parameter | type | required | description |
| ---------- | ------ | -------- | --------------------------------------------- |
| _name_ | string | yes | the name of the service unit |
| _userMode_ | bool | no | to use service in user mode, false by default |
```yaml
service.masked:
- name: ssd-backup
- userMode: true
```
## **service.unmasked**
Checks if a service is unmasked and unmasks it if it is not
#### Parameters
| parameter | type | required | description |
| ---------- | ------ | -------- | --------------------------------------------- |
| _name_ | string | yes | the name of the service unit |
| _userMode_ | bool | no | to use service in user mode, false by default |
```yaml
service.unmasked:
- name: ssd-backup
- userMode: true
```
## **service.running**
Checks if a service is running and starts it if it is not
#### Parameters
| parameter | type | required | description |
| ---------- | ------ | -------- | --------------------------------------------- |
| _name_ | string | yes | the name of the service unit |
| _userMode_ | bool | no | to use service in user mode, false by default |
```yaml
service.running:
- name: docker.service
- userMode: false
```
## **service.stopped**
Checks if a service is stopped and stops it if it is not
#### Parameters
| parameter | type | required | description |
| ---------- | ------ | -------- | --------------------------------------------- |
| _name_ | string | yes | the name of the service unit |
| _userMode_ | bool | no | to use service in user mode, false by default |
```yaml
service.stopped:
- name: docker.service
- userMode: false
```
## **service.enabled**
Checks if a service is enabled and enables it if it is not
#### Parameters
| parameter | type | required | description |
| ---------- | ------ | -------- | --------------------------------------------- |
| _name_ | string | yes | the name of the service unit |
| _userMode_ | bool | no | to use service in user mode, false by default |
```yaml
service.enabled:
- name: cronie.service
- userMode: false
```
## **service.disabled**
Checks if a service is disabled and disables it if it is not
#### Parameters
| parameter | type | required | description |
| ---------- | ------ | -------- | --------------------------------------------- |
| _name_ | string | yes | the name of the service unit |
| _userMode_ | bool | no | to use service in user mode, false by default |
```yaml
service.disabled:
- name: cronie.service
- userMode: false
```
## **service.restarted**
Restarts a service
#### Parameters
| parameter | type | required | description |
| ---------- | ------ | -------- | --------------------------------------------- |
| _name_ | string | yes | the name of the service unit |
| _userMode_ | bool | no | to use service in user mode, false by default |
```yaml
service.restarted:
- name: cronie.service
- userMode: false
```

View File

@@ -0,0 +1,65 @@
---
title: grlx.ingredients.user
description: An overview of grlx.ingredients.user
slug: 1.0/ingredients/user
---
The user ingredient handles user operations on sprouts.
## **user.absent**
Removes a user if it exists
#### Parameters
| parameter | type | required | description |
| --------- | ------ | -------- | ---------------------------------- |
| _name_ | string | yes | the name of the user to be removed |
```yaml
user.absent:
- name: supersprout
```
## **user.exists**
Validates if a user exists
#### Parameters
| parameter | type | required | description |
| --------- | ------ | -------- | --------------------- |
| _name_ | string | yes | The username to check |
```yaml
user.exists:
- name: sproutuser
```
## **user.present**
Creates a new user if it does not exist
#### Parameters
| parameter | type | required | description |
| --------- | ------------ | -------- | -------------------------------------------------------- |
| _name_ | string | yes | The username of the user |
| _uid_ | string | no | The UID assigned to the user |
| _gid_ | string | no | The GID to the user |
| _groups_ | list, string | no | A list groups the user is part of |
| _shell_ | string | no | The user login shell, /bin/false by default |
| _home_ | string | no | The path to the user's home directory if one is required |
```yaml
user.present:
- name: sproutuser
- uid: '1003'
- gid: '1003'
- groups:
- wheel
- media
- sudo
- shell: /bin/bash
- home: /var/sproutuser
```

View File

@@ -0,0 +1,56 @@
---
title: Overview
description: An overview of grlx recipes
slug: 1.0/recipes/overview
---
Recipes are the core of grlx functionality, providing a structured way for developers to deploy actions to sprouts. Recipes are a collection of ingredients which can be executed against a sprout. Recipes must be placed on the farmer at `/srv/grlx/recipes/prod` (with support for other non-prod environments in the future). Furthermore, recipes can utilize [Go's builtin templating engine](https://pkg.go.dev/text/template) to provide dynamic functionality for deployment. Below is a simple example of the makeup of a recipe.
```yaml
include:
- .super-sprout-steps
steps:
{{ if eq (props hostname) super-sprout }}
install super-sprout:
file.touch:
- name: ~/super-sprout-config
requisites:
- require: super-sprout-steps-completed
{{ else }}
install normal-sprout:
file.touch:
- name: ~/normal-sprout-config
{{ end }}
```
In this example, an include is created for a `super-sprout-steps.grlx` file. This file would contain a list of steps with a final step `grlx-sprout-steps-completed`. Next we have our list of steps, bounded by Go templates. This template checks the hostname of the sprout and renders the steps to send to the farmer. In this case, if the sprout's hostname is `super-sprout`, then it will render a template that looks like this:
```yaml
include:
- .super-sprout-steps
steps:
install super-sprout:
file.touch:
- name: ~/super-sprout-config
requisites:
- require: super-sprout-steps-completed
```
If the hostname is anything else, the sprout will run:
```yaml
include:
- .super-sprout-steps
steps:
install normal-sprout:
file.touch:
- name: ~/normal-sprout-config
```
It is important to note the requisites listed. This ensures that the step from the include (`super-sprout-steps-completed`) occurs _before_ the `install-super-sprout` step is run.
:::note
Deployment of recipe steps is non-deterministic unless requisites are provided. If order is important, then requisites **_must_** be specified.
:::
Once you create a recipe, you can run it using `grlx cook <name> -T <list of sprouts or \* for all sprouts>`. In the case of the example above, I might run something like `grlx cook example -T \*` to run the above on all sprouts. You could also run `grlx cook example -T \* --out json` to get a json output of running this recipe against sprouts.

70
src/content/docs/audit.md Normal file
View File

@@ -0,0 +1,70 @@
---
title: Audit Logging
description: Track all farmer actions with per-user attribution
---
grlx records every significant action performed through the farmer in an append-only audit log. Each entry captures who did what, when, on which target, and whether it succeeded.
## What Gets Logged
- Recipe executions (`cook`)
- Command dispatch (`cmd.run`)
- SSH sessions
- Key accept and reject operations
- Configuration changes
Each entry includes:
| Field | Description |
| --------- | -------------------------------- |
| Timestamp | When the action occurred |
| Username | The authenticated user who acted |
| Action | The type of operation |
| Target | The sprout or cohort affected |
| Result | Success or failure with detail |
## Storage
Audit logs are stored as JSONL (one JSON object per line) in the farmer's data directory, partitioned by date. This makes them easy to process with standard tools like `jq`, `grep`, or any log aggregation system.
## CLI Commands
### List available dates
```bash
grlx audit dates
```
Shows all dates that have audit log entries, along with entry counts and file sizes.
### Query audit entries
```bash
# All entries for today
grlx audit list
# Filter by date
grlx audit list --date 2026-03-15
# Filter by action type
grlx audit list --action cook
# Filter by user
grlx audit list --pubkey UABC...XYZ
# Show only failures
grlx audit list --failed
# Limit results
grlx audit list --limit 50
```
### JSON output
```bash
grlx audit list --out json
```
## API Access
The audit log is also available through the farmer's API at `GET /api/v1/audit`, supporting the same query parameters as the CLI. This endpoint requires at least `view` permission.

108
src/content/docs/cohorts.md Normal file
View File

@@ -0,0 +1,108 @@
---
title: Cohorts
description: Group sprouts for targeted operations
---
Cohorts let you organize sprouts into named groups for targeting with `cook`, `cmd run`, `ssh`, and other operations. They replace ad-hoc glob patterns with reusable, manageable groupings.
## Cohort Types
### Static
A fixed list of sprout names:
```toml
[cohorts.webservers]
type = "static"
members = ["web-01", "web-02", "web-03"]
```
### Dynamic
Membership is determined by matching a sprout property at evaluation time:
```toml
[cohorts.linux-hosts]
type = "dynamic"
match_prop = "os"
match_value = "linux"
```
Dynamic cohorts automatically include any sprout whose property matches the condition. Use `grlx cohorts refresh` to re-evaluate membership against currently connected sprouts.
### Compound
Combine other cohorts with boolean logic:
```toml
[cohorts.prod-linux]
type = "compound"
operator = "AND"
operands = ["production", "linux-hosts"]
```
Supported operators:
| Operator | Description |
| -------- | ------------------------------------------------ |
| `AND` | Sprouts that belong to **all** listed cohorts |
| `OR` | Sprouts that belong to **any** listed cohort |
| `EXCEPT` | Sprouts in the first cohort but **not** the rest |
Compound cohorts can reference other compound cohorts (nesting), with a depth limit to prevent cycles.
## CLI Commands
### List all cohorts
```bash
grlx cohorts list
```
### Show cohort details
```bash
grlx cohorts show webservers
```
Displays the cohort type, configuration, and resolved member list.
### Refresh dynamic membership
```bash
# Refresh a specific cohort
grlx cohorts refresh webservers
# Refresh all cohorts
grlx cohorts refresh
```
Re-evaluates dynamic cohort membership against currently connected sprouts and their properties.
### JSON output
All cohort commands support `--out json` for machine-readable output:
```bash
grlx cohorts list --out json
grlx cohorts show webservers --out json
```
## Using Cohorts
Cohorts can be used anywhere you target sprouts:
```bash
# Cook a recipe against a cohort
grlx cook nginx-setup -C webservers
# Run a command on a cohort
grlx cmd run "uptime" -C linux-hosts
# SSH with cohort picker
grlx ssh -C webservers
```
## RBAC Integration
RBAC permissions are assigned per-cohort. A user might have `cook` and `view` permissions on the `staging` cohort but only `view` on `production`. See the [RBAC documentation](/rbac) for details.

View File

@@ -2,45 +2,71 @@
title: Glossary
description: A glossary of terms commonly used in grlx
---
## Farmer (grlx-farmer)
The farmer is your management server running the `grlx-farmer` binary. The binary can run as a systemd service or be hosted in a container. This has analogs in other similar configuration platforms. In SaltStack, this is your Salt Master, or in Chef, your Chef Server. grlx utilizes the farmer for things like authentication, job processing, and [ingredients](/architecture/overview#ingredients).
## Sprout (grlx-sprout)
The sprout is a managed node running the `grlx-sprout` binary. Sprouts receive commands from the farmer over the NATS message bus. These can be immediate shell commands, or actions that are performed by Ingredients defined in a recipe (e.g. ensure that a file exists). Sprouts are similar to a Salt Minion in SaltStack or a Chef Client in Chef.
## CLI (grlx CLI)
The CLI is a tool used to interact with the farmer and manage sprouts. The CLI is used to accept keys from sprouts and `cook` (run) recipes that are hosted on the farmer. The CLI also contains tools to tail the traffic over the NAT bus, test sprout connections, and run arbitrary commands on sprouts.
## Authentication
All communication is encrypted using self-signed TLS certificates and [NATS.io NKey encryption](https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth). Certificates are pinned to the clients on first connection as an extra security precaution. Furthermore, every CLI must accept a working certificate for any interactions with the farmer. NKeys provides a NATS native method for handling asymmetric encryption of messages between all parts of the system.
## Cohorts
[Cohorts](/cohorts) let you organize sprouts into named groups (static, dynamic, or compound) for targeting with `cook`, `cmd run`, `ssh`, and other operations.
## Command Execution
The CLI has the ability to dispatch arbitrary shell commands to sprouts using `grlx cmd run`. Jobs are evaluated and dispatched to sprouts and their outputs are aggregated to the output of the CLI. Furthermore, this can be used inside of recipes for more structured deployments.
## Farmer Hooks (hooks)
Hooks provide a way to fire off webhooks when recipe deployment (cook) starts, fails or finishes. Additionally, you can configure a hook to fire when a new sprout is added to the farm, or when a sprouts status has changed. A “hook” ingredient is also provided (not shown) that can be fired from the sprout during steps of a recipe.
## Ingredients
[Ingredients](/ingredients/overview) are how we build configurations with grlx. They can be thought of as the building blocks for completing various file, service, or management operations. Ingredients use a provider interface to allow for simple extensibility via the [Go plugin system](https://pkg.go.dev/plugin).
## Template Engine
Templating is achieved with [Gos builtin templating engine](https://pkg.go.dev/text/template). This provides a Jinja-like templating system that hooks into the Farmer and Sprout properties to customize recipes to meet the dynamic needs of your needs.
## Job Enqueuing and Processing
You initiate jobs from the CLI by cooking recipes. Processing is continued on the farmer, and sprouts stream results to the CLI in real-time over the message bus.
## Properties (Farmer, Sprout and Fetched Sprout Props)
Both farmers and sprouts have properties for use in recipes. These properties are used in recipe templates to specify conditional execution of parts of the recipe. One example of this might be the specific OS or architecture of a sprout to ensure that the correct package gets installed on the correct machine.
## RBAC
[Role-based access control](/rbac) governs which users can perform which actions on which cohorts. Permissions are tied to CLI public keys. See also [Audit Logging](/audit) for action tracking.
## SSH
[`grlx ssh`](/ssh) opens an interactive shell on a sprout over the NATS message bus — no direct network access required.
## API
Each farmer has a REST API used for querying information about jobs or other information the farmer has access to. API tokens can be generated by the `grlx auth token`.
Each farmer has a REST API used for querying information about jobs or other information the farmer has access to. API tokens can be generated by the `grlx auth token`.
## Local API
The Local API is hosted on the grlx CLI to run a web frontend application for grlx. The local API provides a simple yet extensible interface for interacting with the CLI without the need for a terminal.
## Message Bus
An embedded [NATS.io server](https://nats.io/) is built into the farmer to handle connection between the sprouts, farmer, and the CLI. The bus uses NKeys to handle secure message passing between sprouts, the farmer, and CLIs. The message bus is responsible for handling communication between the CLI and the farmer as well as ensuring that jobs get dispatched to sprouts.
An embedded [NATS.io server](https://nats.io/) is built into the farmer to handle connection between the sprouts, farmer, and the CLI. The bus uses NKeys to handle secure message passing between sprouts, the farmer, and CLIs. The message bus is responsible for handling communication between the CLI and the farmer as well as ensuring that jobs get dispatched to sprouts.
## Managed Node
The managed node is where the `grlx-sprout` daemon runs. It is the physical device where a sprout will deploy files, manage services, or run commands.
The managed node is where the `grlx-sprout` daemon runs. It is the physical device where a sprout will deploy files, manage services, or run commands.

View File

@@ -1,28 +1,33 @@
---
title: grlx.ingredients.cmd
description: cmd
description: cmd
---
The cmd ingredient allows you to run arbitrary shell commands against sprouts.
## **cmd.run**
Runs shell commands against a sprout
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| _name_ | string | true | the command to run
| _runas_ | string | false | user who will run this command (defaults to root)
| _path_ | string | false | the path to the binary, this is prepended to the name
| _cwd_ | string | false | the directory to run this command
| _env_ | list | false | environment variables to set should be specified like key1=value1
| _timeout_ | string | false | set a timeout for the command using [Go Duration](https://pkg.go.dev/time#ParseDuration)
| parameter | type | required | description |
| --------- | ------ | -------- | ---------------------------------------------------------------------------------------- |
| _name_ | string | true | the command to run |
| _runas_ | string | false | user who will run this command (defaults to root) |
| _path_ | string | false | the path to the binary, this is prepended to the name |
| _cwd_ | string | false | the directory to run this command |
| _env_ | list | false | environment variables to set should be specified like key1=value1 |
| _timeout_ | string | false | set a timeout for the command using [Go Duration](https://pkg.go.dev/time#ParseDuration) |
```yaml
cmd.run:
- name: go version
- runas: super-cool-user
- path: /usr/local/bin
- cwd: /tmp
- env:
- name: go version
- runas: super-cool-user
- path: /usr/local/bin
- cwd: /tmp
- env:
- GOOS=linux
- GOARCH=arm64
- timeout: 10s
- timeout: 10s
```

View File

@@ -2,7 +2,9 @@
title: grlx.ingredients.file.providers
description: grlx built-in file providers
---
`grlx` has a concept of file providers for different ways that you might obtain a file to be added to a given sprout. This uses a provider interface to keep this extensible and provide a standard way to use different file types. The `go` interface for File Providers looks like the following:
```go
type FileProvider interface {
Download(context.Context) error
@@ -12,22 +14,29 @@ type FileProvider interface {
Verify(context.Context) (bool, error)
}
```
By default, `grlx` has two built-in providers: local and HTTP.
By default, `grlx` has two built-in providers: local and HTTP.
## Local
The local provider would be how you would use a file from your host system.
#### Example
```yaml
file.cached:
- source: go1.21.3.src.tar.gz
- hash: sha256=186f2b6f8c8b704e696821b09ab2041a5c1ee13dcbc3156a13adcf75931ee488
- source: go1.21.3.src.tar.gz
- hash: sha256=186f2b6f8c8b704e696821b09ab2041a5c1ee13dcbc3156a13adcf75931ee488
```
## HTTP
The HTTP provider allows you to download files via HTTP for use. The [example](/ingredients/file-providers/#example) below is using the HTTP provider to download Go from the Internet. This file then gets cached to the sprout.
The HTTP provider allows you to download files via HTTP for use. The [example](/ingredients/file-providers/#example) below is using the HTTP provider to download Go from the Internet. This file then gets cached to the sprout.
#### Example
```yaml
file.cached:
- source: https://go.dev/dl/go1.21.3.src.tar.gz
- hash: sha256=186f2b6f8c8b704e696821b09ab2041a5c1ee13dcbc3156a13adcf75931ee488
- source: https://go.dev/dl/go1.21.3.src.tar.gz
- hash: sha256=186f2b6f8c8b704e696821b09ab2041a5c1ee13dcbc3156a13adcf75931ee488
```

View File

@@ -2,38 +2,52 @@
title: grlx.ingredients.group
description: An overview of grlx.ingredients.group
---
The group ingredient handles group operations on sprouts.
## **group.absent**
Removes a group
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| _name_ | string | no | The name of the group to remove
| parameter | type | required | description |
| --------- | ------ | -------- | ------------------------------- |
| _name_ | string | no | The name of the group to remove |
```yaml
group.absent:
- name: sproutgroup
```
## **group.exists**
Validates if a group exists
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| _name_ | string | yes | The group name to check
| parameter | type | required | description |
| --------- | ------ | -------- | ----------------------- |
| _name_ | string | yes | The group name to check |
```yaml
group.exists:
- name: sproutgroup
```
## **group.present**
Creates a group if it does not exist
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| _name_ | string | yes | The name of the group to create
| _gid_ | string | no | The GID of the group to create
| parameter | type | required | description |
| --------- | ------ | -------- | ------------------------------- |
| _name_ | string | yes | The name of the group to create |
| _gid_ | string | no | The GID of the group to create |
```yaml
group.present:
- name: sproutgroup
- gid: "1107"
- gid: '1107'
```

View File

@@ -2,9 +2,10 @@
title: Overview
description: The foundation of grlx
sidebar:
order: 1
order: 1
---
Recipe ingredients are how we build configurations with `grlx`. They can be thought of as the building blocks for completing various file, service, or management operations. Recipes rely on dependency injection via a provider system to provide extensible backends for various tasks. This also allows users to develop [go plugins](https://pkg.go.dev/plugin) to extend ingredient providers.
Recipe ingredients are how we build configurations with `grlx`. They can be thought of as the building blocks for completing various file, service, or management operations. Recipes rely on dependency injection via a provider system to provide extensible backends for various tasks. This also allows users to develop [go plugins](https://pkg.go.dev/plugin) to extend ingredient providers.
:::note
The [file ingredient](/ingredients/file) already supports a few different file providers. Check out the list of file providers [here](/ingredients/file-providers).
:::

View File

@@ -2,7 +2,9 @@
title: grlx.ingredients.service.providers
description: grlx built-in service providers
---
grlx has a concept of service providers for different ways to interact with a sprout's various services. This uses a provider interface to keep this extensible and provide a standard way to interact with different service managers. The Go interface for Service Providers looks like the following:
```go
type ServiceProvider interface {
Properties() (map[string]interface{}, error)
@@ -35,6 +37,7 @@ grlx automatically detects which init system is running on each sprout and selec
The `systemd` provider manages services on Linux systems using systemd. This is the most common provider and is auto-detected on systems where `systemctl` is available.
#### Example
```yaml
service.enabled:
- name: cronie.service
@@ -46,11 +49,13 @@ service.enabled:
The `rc.d` provider manages services on BSD systems (FreeBSD, NetBSD, OpenBSD, DragonFlyBSD) using the native rc.d init system. It is auto-detected on systems where `/etc/rc.d` or `/usr/local/etc/rc.d` is present.
The rc.d provider maps grlx service operations to the corresponding `service(8)` and `rc.conf` commands:
- **Start/Stop/Restart** — `service <name> start|stop|restart`
- **Enable/Disable** — adds or removes `<name>_enable="YES"` in `/etc/rc.conf`
- **Status** — `service <name> status`
#### Example
```yaml
service.running:
- name: sshd

View File

@@ -2,50 +2,63 @@
title: grlx.ingredients.user
description: An overview of grlx.ingredients.user
---
The user ingredient handles user operations on sprouts.
## **user.absent**
Removes a user if it exists
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| _name_ | string | yes | the name of the user to be removed
| parameter | type | required | description |
| --------- | ------ | -------- | ---------------------------------- |
| _name_ | string | yes | the name of the user to be removed |
```yaml
user.absent:
- name: supersprout
```
## **user.exists**
Validates if a user exists
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| _name_ | string | yes | The username to check
| parameter | type | required | description |
| --------- | ------ | -------- | --------------------- |
| _name_ | string | yes | The username to check |
```yaml
user.exists:
- name: sproutuser
```
## **user.present**
Creates a new user if it does not exist
#### Parameters
| parameter | type | required | description |
|-----------|------|----------|-------------|
| _name_ | string | yes | The username of the user
| _uid_ | string | no | The UID assigned to the user
| _gid_ | string | no | The GID to the user
| _groups_ | list, string | no | A list groups the user is part of
| _shell_ | string | no | The user login shell, /bin/false by default
| _home_ | string | no | The path to the user's home directory if one is required
| parameter | type | required | description |
| --------- | ------------ | -------- | -------------------------------------------------------- |
| _name_ | string | yes | The username of the user |
| _uid_ | string | no | The UID assigned to the user |
| _gid_ | string | no | The GID to the user |
| _groups_ | list, string | no | A list groups the user is part of |
| _shell_ | string | no | The user login shell, /bin/false by default |
| _home_ | string | no | The path to the user's home directory if one is required |
```yaml
user.present:
- name: sproutuser
- uid: "1003"
- gid: "1003"
- uid: '1003'
- gid: '1003'
- groups:
- wheel
- media
- sudo
- wheel
- media
- sudo
- shell: /bin/bash
- home: /var/sproutuser
```

131
src/content/docs/rbac.md Normal file
View File

@@ -0,0 +1,131 @@
---
title: RBAC
description: Role-based access control for users and cohorts
---
grlx includes a role-based access control (RBAC) system that governs who can perform actions on which sprouts. Permissions are tied to public keys, so every CLI user has an auditable identity.
## Concepts
- **User**: Identified by their CLI public key. Each key maps to a username and role.
- **Role**: A named set of permissions. Roles define which actions a user can perform on which cohorts.
- **Action**: An operation type — `cook`, `cmd`, `ssh`, or `view`.
- **Cohort scope**: Permissions are granted per-cohort. A user may have different permissions on different cohorts.
## Configuration
Roles and users are defined in the farmer configuration file:
```toml
[rbac.roles.operator]
actions = ["cook", "cmd", "ssh", "view"]
cohorts = ["*"]
[rbac.roles.deployer]
actions = ["cook", "view"]
cohorts = ["staging", "production"]
[rbac.roles.viewer]
actions = ["view"]
cohorts = ["*"]
```
Users are mapped to roles by their public key:
```toml
[[rbac.users]]
name = "alice"
pubkey = "UABC...XYZ"
role = "operator"
[[rbac.users]]
name = "bob"
pubkey = "UDEF...UVW"
role = "deployer"
```
### Built-in Viewer Role
Users with only the `view` action can:
- List sprouts, jobs, props, cohorts
- Read job details and audit logs
They **cannot** cook recipes, run commands, or open SSH sessions.
## Actions
| Action | Description |
| ------ | ------------------------------------ |
| `cook` | Execute recipes on sprouts |
| `cmd` | Run arbitrary commands via `cmd run` |
| `ssh` | Open interactive shell sessions |
| `view` | Read-only access to all resources |
## Validation
The farmer validates the RBAC configuration at startup:
- Referenced cohorts must exist in the cohort configuration
- Public keys must be unique across all users
- Role names must be unique
If validation fails, the farmer will not start and will report the configuration errors.
## CLI Commands
### Check your identity
```bash
grlx auth whoami
```
Shows the current CLI user's public key and assigned role.
### List users
```bash
grlx auth users
```
### List roles
```bash
grlx auth roles
```
### Explain effective permissions
```bash
grlx auth explain
```
Shows the current user's effective permissions across all cohorts — which actions are allowed where.
## Enforcement
Every request to the farmer is checked against RBAC before execution. If the requesting user lacks the required action on the target cohort, the request is denied with a clear error message.
The enforcement middleware covers:
- `cook` — recipe execution
- `cmd.run` — arbitrary command dispatch
- `ssh` — interactive shell sessions
- Read endpoints — restricted to users with at least `view` permission
## `dangerously_allow_root`
For development or single-user setups, the farmer supports a `dangerously_allow_root` flag that bypasses RBAC checks:
```toml
[farmer]
dangerously_allow_root = true
```
:::caution
This disables all permission checks. Do not use in production environments. The farmer logs a startup warning when this flag is enabled.
:::
## Audit Logging
All actions are recorded in the audit log with the acting user's identity. See [Audit Logging](/audit) for details.

View File

@@ -2,7 +2,9 @@
title: Overview
description: An overview of grlx recipes
---
Recipes are the core of grlx functionality, providing a structured way for developers to deploy actions to sprouts. Recipes are a collection of ingredients which can be executed against a sprout. Recipes must be placed on the farmer at `/srv/grlx/recipes/prod` (with support for other non-prod environments in the future). Furthermore, recipes can utilize [Go's builtin templating engine](https://pkg.go.dev/text/template) to provide dynamic functionality for deployment. Below is a simple example of the makeup of a recipe.
```yaml
include:
- .super-sprout-steps
@@ -19,7 +21,9 @@ steps:
- name: ~/normal-sprout-config
{{ end }}
```
In this example, an include is created for a `super-sprout-steps.grlx` file. This file would contain a list of steps with a final step `grlx-sprout-steps-completed`. Next we have our list of steps, bounded by Go templates. This template checks the hostname of the sprout and renders the steps to send to the farmer. In this case, if the sprout's hostname is `super-sprout`, then it will render a template that looks like this:
```yaml
include:
- .super-sprout-steps
@@ -30,7 +34,9 @@ steps:
requisites:
- require: super-sprout-steps-completed
```
If the hostname is anything else, the sprout will run:
```yaml
include:
- .super-sprout-steps
@@ -40,9 +46,9 @@ steps:
- name: ~/normal-sprout-config
```
It is important to note the requisites listed. This ensures that the step from the include (`super-sprout-steps-completed`) occurs _before_ the `install-super-sprout` step is run.
It is important to note the requisites listed. This ensures that the step from the include (`super-sprout-steps-completed`) occurs _before_ the `install-super-sprout` step is run.
:::note
Deployment of recipe steps is non-deterministic unless requisites are provided. If order is important, then requisites ***must*** be specified.
Deployment of recipe steps is non-deterministic unless requisites are provided. If order is important, then requisites **_must_** be specified.
:::
Once you create a recipe, you can run it using `grlx cook <name> -T <list of sprouts or \* for all sprouts>`. In the case of the example above, I might run something like `grlx cook example -T \*` to run the above on all sprouts. You could also run `grlx cook example -T \* --out json` to get a json output of running this recipe against sprouts.

View File

@@ -0,0 +1,46 @@
---
title: Managing Sprouts
description: List, inspect, and monitor connected sprouts
---
The `grlx sprouts` command provides visibility into all sprouts known to the farmer — their connection status, key state, and properties.
## List Sprouts
```bash
grlx sprouts list
```
Shows all known sprouts sorted by connection status (connected first, then alphabetical). The output includes each sprout's name, connection state, and key state.
### Filter by state
```bash
# Only show sprouts with accepted keys
grlx sprouts list --state accepted
# Only show currently connected sprouts
grlx sprouts list --online
```
### JSON output
```bash
grlx sprouts list --out json
```
## Inspect a Sprout
```bash
grlx sprouts show my-sprout
```
Displays detailed information about a single sprout, including its properties, connection history, and current status.
## Key Management
Before a sprout can receive commands, the farmer must accept its key. This is handled through the `grlx auth` subcommands rather than the sprouts command — see the [Authentication section](/glossary#authentication) for details.
## Connection Model
Sprouts connect to the farmer's embedded NATS server on startup. The farmer tracks which sprouts are currently connected and their last-seen timestamps. Sprouts behind firewalls or NAT work fine — they only need outbound access to the farmer's NATS port.

51
src/content/docs/ssh.md Normal file
View File

@@ -0,0 +1,51 @@
---
title: SSH
description: Open interactive shell sessions on sprouts over NATS
---
grlx provides a built-in remote shell that works entirely over the NATS message bus. No direct SSH access or network connectivity to the sprout is required — the farmer validates the request and the sprout spawns a PTY-backed shell process.
## Basic Usage
Connect to a sprout by name:
```bash
grlx ssh my-sprout
```
This opens an interactive terminal session. Press `Ctrl-D` or type `exit` to end it.
### Specifying a Shell
By default the sprout uses `/bin/sh`. Override with `--shell`:
```bash
grlx ssh my-sprout --shell /bin/bash
```
## Cohort Targeting
Instead of naming a sprout directly, target a cohort with `-C` / `--cohort`:
```bash
grlx ssh -C webservers
```
If the cohort resolves to a single sprout, the connection opens immediately. If multiple sprouts match, an interactive picker is displayed so you can choose which one to connect to.
:::note
You cannot specify both a sprout name and `--cohort` at the same time.
:::
## How It Works
1. The CLI sends a shell request to the farmer over NATS.
2. The farmer validates the requesting user's identity and RBAC permissions.
3. The target sprout spawns a PTY-backed shell process.
4. Input and output are relayed through NATS in real time.
Because the connection runs over the message bus, sprouts behind firewalls or NAT are fully accessible — as long as they can reach the farmer's NATS server.
## RBAC
Shell access is governed by the `ssh` action in the RBAC permission model. Users without the `ssh` permission on the target sprout's cohort will be denied. See the [RBAC documentation](/rbac) for details on configuring permissions.

View File

@@ -0,0 +1,30 @@
{
"sidebar": [
{
"label": "Getting Started",
"link": "/getting-started"
},
{
"label": "Architecture",
"autogenerate": {
"directory": "architecture"
}
},
{
"label": "Ingredients",
"autogenerate": {
"directory": "ingredients"
}
},
{
"label": "Recipes",
"autogenerate": {
"directory": "recipes"
}
},
{
"label": "Glossary",
"link": "/glossary"
}
]
}

View File

@@ -1,9 +1,9 @@
.discord-bg .card .icon{
background-color: #5865F2;
--sl-card-border: #454FBF !important;
.discord-bg .card .icon {
background-color: #5865f2;
--sl-card-border: #454fbf !important;
}
[data-theme="light"] .hidden{
[data-theme='light'] .hidden {
display: none;
}