From 19923845c079c745f1988731a43402ceae199704 Mon Sep 17 00:00:00 2001 From: Wanjohi <71614375+wanjohiryan@users.noreply.github.com> Date: Tue, 20 Aug 2024 15:12:00 +0300 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20[MAJOR]=20Rebrand=20(#101)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description **What issue are you solving (or what feature are you adding) and how are you doing it?** 1. Netris has been renamed and rebranded to Nestri. 2. New Logo and colors, plus a new Philosophy and mission(more on that later) 3. We are moving all different repos into this one - which means API, Docker, Website, Docs etc will be moved here --- .github/CODEOWNERS | 1 - .github/dependabot.yml | 18 - .github/labeler.yml | 119 - .github/labels.yml | 108 - .github/pull_request_template.md | 3 - .github/release-drafter.yml | 45 - .github/workflows/autolabeler.yml | 93 - .github/workflows/base.yml | 135 - .github/workflows/labels.yml | 33 - .github/workflows/relay.yml | 135 - .github/workflows/release-drafter.yml | 16 - .github/workflows/server.yml | 137 - .github/workflows/warp-input.yml | 236 - .github/workflows/warp.yml | 234 - .gitignore | 38 + .gitmodules | 6 - .npmrc | 0 .scripts/entrypoint.sh | 285 - .scripts/proton | 610 -- .scripts/startup.sh | 11 - .scripts/supervisord.conf | 42 - .vscode/settings.json | 3 - LICENSE | 4 +- README.md | 190 +- apps/docs/.eslintrc.js | 9 + apps/docs/.gitignore | 36 + apps/docs/README.md | 36 + apps/docs/app/favicon.ico | Bin 0 -> 25931 bytes apps/docs/app/fonts/GeistMonoVF.woff | Bin 0 -> 67864 bytes apps/docs/app/fonts/GeistVF.woff | Bin 0 -> 66268 bytes apps/docs/app/globals.css | 39 + apps/docs/app/layout.tsx | 31 + apps/docs/app/page.module.css | 188 + apps/docs/app/page.tsx | 99 + apps/docs/next.config.mjs | 4 + apps/docs/package.json | 27 + apps/docs/public/file-text.svg | 3 + apps/docs/public/globe.svg | 10 + apps/docs/public/next.svg | 1 + apps/docs/public/vercel.svg | 10 + apps/docs/public/window.svg | 3 + apps/docs/tsconfig.json | 18 + .../www/.create-qwik-28s64ekndxx/package.json | 46 - apps/www/.eslintrc.cjs | 2 +- apps/www/.gitignore | 3 - apps/www/.prettierrc.js | 3 - apps/www/README.md | 64 +- apps/www/adapters/vercel-edge/vite.config.ts | 16 - apps/www/package.json | 42 +- apps/www/pnpm-lock.yaml | 9347 ----------------- apps/www/postcss.config.cjs | 6 - apps/www/public/favicon.png | Bin 23387 -> 0 bytes apps/www/public/favicon.svg | 1 + apps/www/public/images/next.png | Bin 0 -> 60716 bytes apps/www/qwik.env.d.ts | 4 + .../components/router-head/router-head.tsx | 20 +- apps/www/src/entry.preview.tsx | 1 + apps/www/src/entry.ssr.tsx | 3 + apps/www/src/entry.vercel-edge.tsx | 22 - apps/www/src/global.css | 15 - apps/www/src/root.tsx | 15 +- apps/www/src/routes/index.tsx | 23 +- apps/www/src/routes/layout.tsx | 14 +- apps/www/src/routes/service-worker.ts | 2 +- apps/www/tailwind.config.js | 26 - apps/www/tsconfig.json | 3 +- apps/www/vercel.json | 13 - apps/www/vite.config.ts | 88 +- assets/banner.png | Bin 2271765 -> 0 bytes assets/logo-name-black.png | Bin 1547 -> 0 bytes assets/logo-name-white.png | Bin 1481 -> 0 bytes assets/logo.png | Bin 13417 -> 0 bytes assets/star-us.png | Bin 47037 -> 0 bytes base.Dockerfile | 296 - bin/input/.gitignore | 1 - bin/input/Cargo.lock | 1701 --- bin/input/Cargo.toml | 21 - bin/input/src/cli.rs | 43 - bin/input/src/input.rs | 277 - bin/input/src/main.rs | 121 - bun.lockb | Bin 0 -> 339948 bytes cli | 1 - dev/build.sh | 3 - dev/run-input.sh | 3 - dev/run.sh | 5 - dev/script.sh | 20 - moq-server | 1 - package.json | 23 + packages/eslint-config/README.md | 3 + packages/eslint-config/library.js | 34 + packages/eslint-config/next.js | 35 + packages/eslint-config/package.json | 19 + packages/eslint-config/react-internal.js | 39 + packages/typescript-config/base.json | 20 + packages/typescript-config/nextjs.json | 13 + packages/typescript-config/package.json | 9 + packages/typescript-config/react-library.json | 8 + packages/ui/.eslintrc.js | 10 + packages/ui/package.json | 28 + packages/ui/src/button.tsx | 20 + packages/ui/src/card.tsx | 25 + packages/ui/src/code.tsx | 9 + packages/ui/tsconfig.json | 8 + packages/ui/tsconfig.lint.json | 8 + packages/ui/turbo/generators/config.ts | 30 + .../turbo/generators/templates/component.hbs | 8 + relay.Dockerfile | 23 - server.Dockerfile | 88 - turbo.json | 42 + warp-input.Dockerfile | 14 - warp.Dockerfile | 13 - 111 files changed, 1115 insertions(+), 14702 deletions(-) delete mode 100644 .github/CODEOWNERS delete mode 100644 .github/dependabot.yml delete mode 100644 .github/labeler.yml delete mode 100644 .github/labels.yml delete mode 100644 .github/pull_request_template.md delete mode 100644 .github/release-drafter.yml delete mode 100644 .github/workflows/autolabeler.yml delete mode 100644 .github/workflows/base.yml delete mode 100644 .github/workflows/labels.yml delete mode 100644 .github/workflows/relay.yml delete mode 100644 .github/workflows/release-drafter.yml delete mode 100644 .github/workflows/server.yml delete mode 100644 .github/workflows/warp-input.yml delete mode 100644 .github/workflows/warp.yml create mode 100644 .gitignore delete mode 100644 .gitmodules create mode 100644 .npmrc delete mode 100644 .scripts/entrypoint.sh delete mode 100644 .scripts/proton delete mode 100644 .scripts/startup.sh delete mode 100644 .scripts/supervisord.conf delete mode 100644 .vscode/settings.json create mode 100644 apps/docs/.eslintrc.js create mode 100644 apps/docs/.gitignore create mode 100644 apps/docs/README.md create mode 100644 apps/docs/app/favicon.ico create mode 100644 apps/docs/app/fonts/GeistMonoVF.woff create mode 100644 apps/docs/app/fonts/GeistVF.woff create mode 100644 apps/docs/app/globals.css create mode 100644 apps/docs/app/layout.tsx create mode 100644 apps/docs/app/page.module.css create mode 100644 apps/docs/app/page.tsx create mode 100644 apps/docs/next.config.mjs create mode 100644 apps/docs/package.json create mode 100644 apps/docs/public/file-text.svg create mode 100644 apps/docs/public/globe.svg create mode 100644 apps/docs/public/next.svg create mode 100644 apps/docs/public/vercel.svg create mode 100644 apps/docs/public/window.svg create mode 100644 apps/docs/tsconfig.json delete mode 100644 apps/www/.create-qwik-28s64ekndxx/package.json delete mode 100644 apps/www/.prettierrc.js delete mode 100644 apps/www/adapters/vercel-edge/vite.config.ts delete mode 100644 apps/www/pnpm-lock.yaml delete mode 100644 apps/www/postcss.config.cjs delete mode 100644 apps/www/public/favicon.png create mode 100644 apps/www/public/favicon.svg create mode 100644 apps/www/public/images/next.png create mode 100644 apps/www/qwik.env.d.ts delete mode 100644 apps/www/src/entry.vercel-edge.tsx delete mode 100644 apps/www/tailwind.config.js delete mode 100644 apps/www/vercel.json delete mode 100644 assets/banner.png delete mode 100644 assets/logo-name-black.png delete mode 100644 assets/logo-name-white.png delete mode 100644 assets/logo.png delete mode 100644 assets/star-us.png delete mode 100644 base.Dockerfile delete mode 100644 bin/input/.gitignore delete mode 100644 bin/input/Cargo.lock delete mode 100644 bin/input/Cargo.toml delete mode 100644 bin/input/src/cli.rs delete mode 100644 bin/input/src/input.rs delete mode 100644 bin/input/src/main.rs create mode 100755 bun.lockb delete mode 160000 cli delete mode 100644 dev/build.sh delete mode 100644 dev/run-input.sh delete mode 100644 dev/run.sh delete mode 100644 dev/script.sh delete mode 160000 moq-server create mode 100644 package.json create mode 100644 packages/eslint-config/README.md create mode 100644 packages/eslint-config/library.js create mode 100644 packages/eslint-config/next.js create mode 100644 packages/eslint-config/package.json create mode 100644 packages/eslint-config/react-internal.js create mode 100644 packages/typescript-config/base.json create mode 100644 packages/typescript-config/nextjs.json create mode 100644 packages/typescript-config/package.json create mode 100644 packages/typescript-config/react-library.json create mode 100644 packages/ui/.eslintrc.js create mode 100644 packages/ui/package.json create mode 100644 packages/ui/src/button.tsx create mode 100644 packages/ui/src/card.tsx create mode 100644 packages/ui/src/code.tsx create mode 100644 packages/ui/tsconfig.json create mode 100644 packages/ui/tsconfig.lint.json create mode 100644 packages/ui/turbo/generators/config.ts create mode 100644 packages/ui/turbo/generators/templates/component.hbs delete mode 100644 relay.Dockerfile delete mode 100644 server.Dockerfile create mode 100644 turbo.json delete mode 100644 warp-input.Dockerfile delete mode 100644 warp.Dockerfile diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index d73852fa..00000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @wanjohiryan \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 57ada58e..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: 2 -updates: - - package-ecosystem: 'gitsubmodule' - directory: '/' - schedule: - interval: 'daily' - open-pull-requests-limit: 10 - labels: - - 'type/build' - - package-ecosystem: 'github-actions' - directory: '/' - schedule: - interval: 'weekly' - labels: - - 'type/build' - ignore: - - dependency-name: "*" - update-types: ["version-update:semver-patch"] \ No newline at end of file diff --git a/.github/labeler.yml b/.github/labeler.yml deleted file mode 100644 index f0ad6f3b..00000000 --- a/.github/labeler.yml +++ /dev/null @@ -1,119 +0,0 @@ -version: 2 -labels: - - label: 'type/chore' - branch: '^chore/.*' - title: '^\s*.*?\schore(?:(.+))?!?:' - - - label: 'type/fix' - branch: '^fix/.*' - title: '^\s*.*?\sfix(?:(.+))?!?:' - - - label: 'type/feat' - branch: '^feat/.*' - title: '^\s*.*?\sfeat(?:(.+))?!?:' - - - label: 'type/breaking' - body: '(?i)breaking.*' - - - label: 'type/docs' - branch: '^docs/.*' - title: '^\s*.*?\sdocs(?:(.+))?!?:' - branch: '^docs/.*' - files: - - '**/*.md$' - - '**/*.mdx$' - - - label: 'type/ci' - branch: '^ci/.*' - title: '^\s*.*?\sci(?:(.+))?!?:' - files: - - '.github/.+' - - - label: 'type/build' - branch: '^dependabot/.*' - title: '^\s*.*?\sbuild(?:(.+))?!?:' - - - label: 'type/perf' - title: '^\s*.*?\sperf(?:(.+))?!?:' - - - label: 'mergeable/false' - mergeable: False - - - label: 'usr/dependabot' - branch: '^dependabot/.*' - authors: - - 'dependabot[bot]' - - - label: 'scope/back-end' - files: - - 'server.Dockerfile' - - - label: 'scope/base' - files: - - 'base.Dockerfile' - - - label: 'scope/ffmpeg' - files: - - 'ffmpeg.Dockerfile' - - - label: 'scope/recorder' - files: - - 'recorder.Dockerfile' - - - label: 'scope/relay' - files: - - 'relay.Dockerfile' - - - label: 'scope/front-end' - files: - - 'apps/www/.+' - - - label: 'pkg/www' - files: - - 'apps/www/.+' - - - label: 'scope/git' - files: - - '**/.gitignore$' - - '.github/.+' - - - label: "size/xs" - size: - below: 10 - exclude-files: ["pnpm-lock.yml","yarn.lock"] - - - label: "size/s" - size: - above: 9 - below: 100 - exclude-files: ["pnpm-lock.yml","yarn.lock"] - - - label: "size/m" - size: - above: 49 - below: 200 - exclude-files: ["pnpm-lock.yml","yarn.lock"] - - - label: "size/l" - size: - above: 199 - below: 500 - exclude-files: ["pnpm-lock.yml","yarn.lock"] - - - label: "size/xl" - size: - above: 499 - below: 1000 - exclude-files: ["pnpm-lock.yml","yarn.lock"] - - - label: "size/xxl" - size: - above: 999 - exclude-files: ["pnpm-lock.yml","yarn.lock"] - - - label: "usr/wanjohi" - authors: ['wanjohiryan'] - - - label: "usr/unknown" - negate: True - authors: ['wanjohiryan','apps/dependabot','dependabot', 'dependabot[bot]'] \ No newline at end of file diff --git a/.github/labels.yml b/.github/labels.yml deleted file mode 100644 index 4e3fc07a..00000000 --- a/.github/labels.yml +++ /dev/null @@ -1,108 +0,0 @@ -# Default labels -- name: "type/fix" - color: "B60205" - -- name: "type/build" - color: "6C55D7" - -- name: "type/feat" - color: "0ADE12" - -- name: "type/docs" - color: "891059" - -- name: "type/refactor" - color: "8D44DF" - -- name: "type/revert" - color: "5319e7" - -- name: "type/style" - color: "D71964" - -- name: "type/test" - color: "0B9EE8" - -- name: "type/breaking" - color: "0590CC" - -- name: "type/chore" - color: "B44A63" - -- name: "type/ci" - color: "4FE1A6" - -- name: "type/perf" - color: "4FE1A6" - -- name: "mergeable/false" - color: "B60205" - -- name: "priority/high" - color: "D4E734" - -- name: "priority/mid" - color: "D4E734" - -- name: "priority/low" - color: "D4E734" - -- name: "reg/docker" - color: "5319e7" - -- name: "reg/npm" - color: "5319e7" - -- name: "pkg/www" - color: "5319e7" - -- name: "pkg/lib" - color: "5319e7" - -- name: "pkg/aws" - color: "5319e7" - -- name: "scope/git" - color: "B61B66" - -- name: "scope/infra" - color: "B61B66" - -- name: "scope/front-end" - color: "B61B66" - -- name: "scope/relay" - color: "B61B66" - -- name: "scope/base" - color: "B61B66" - -- name: "scope/back-end" - color: "B61B66" - -- name: "size/xs" - color: "AD4322" - -- name: "size/s" - color: "AD4322" - -- name: "size/m" - color: "AD4322" - -- name: "size/l" - color: "AD4322" - -- name: "size/xl" - color: "AD4322" - -- name: "size/xxl" - color: "AD4322" - -- name: "usr/wanjohi" - color: "09469C" - -- name: "usr/dependabot" - color: "09469C" - -- name: "usr/unknown" - color: "09469C" \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index dad5f890..00000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,3 +0,0 @@ -## Description - -**What issue are you solving (or what feature are you adding) and how are you doing it?** \ No newline at end of file diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index 4eaae2b7..00000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,45 +0,0 @@ -name-template: 'v$RESOLVED_VERSION' -tag-template: 'v$RESOLVED_VERSION' -template: | - # What's Changed - - $CHANGES - - **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION -categories: - - title: '⚠ Breaking Changes' - label: 'type/breaking' - - title: '🚀 New Features' - label: 'type/feat' - - title: '🐜 Bug Fixes' - label: 'type/fix' - - title: '🧰 Maintenance' - label: 'type/chore' - - title: '📖 Documentation' - label: 'type/docs' - - title: '⬆ Version Upgrades' - label: 'type/build' - collapse-after: 10 - - title: 'Other changes' - collapse-after: 10 - -version-resolver: - major: - labels: - - 'type/breaking' - minor: - labels: - - 'type/feat' - patch: - labels: - - 'type/fix' - - 'type/build' - - 'type/docs' - - 'type/chore' - - 'type/refactor' - - 'type/ci' - - 'type/style' - - 'type/test' - -exclude-labels: - - 'skip-changelog' \ No newline at end of file diff --git a/.github/workflows/autolabeler.yml b/.github/workflows/autolabeler.yml deleted file mode 100644 index 69e30346..00000000 --- a/.github/workflows/autolabeler.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: Pull request auto-labeller - -on: - pull_request: - types: - - labeled - - opened - - synchronize - - reopened - - ready_for_review - - edited - -concurrency: - group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - validate: - name: Validate PR title - runs-on: ubuntu-latest - permissions: - pull-requests: write - steps: - - uses: amannn/action-semantic-pull-request@cfb60706e18bc85e8aec535e3c577abe8f70378e - id: lint_pr_title - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - types: |- - build - chore - ci - deprecate - docs - feat - fix - perf - refactor - remove - revert - security - style - test - requireScope: false - # Ensures the subject start with an uppercase character. - subjectPattern: ^([A-Z]).+$ - headerPattern: '^\s*.*?\s(\w*)(?:\(([\w$.\-*/ ]*)\))?: (.*)$' - headerPatternCorrespondence: type, scope, subject - subjectPatternError: | - The subject "{subject}" found in the pull request title "{title}" - didn't match the configured pattern. Please ensure that the subject - starts with an uppercase character - - - uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 - # When the previous steps fails, the workflow would stop. By adding this - # condition you can continue the execution with the populated error message. - if: always() && (steps.lint_pr_title.outputs.error_message != null) - with: - header: pr-title-lint-error - message: | - Hey there and thank you for opening this pull request! 👋🏼 - - We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted. - Additionally, the subject of the title must start with an uppercase character (e.g. feat: New `search` component). - - ``` - ${{ steps.lint_pr_title.outputs.error_message }} - ``` - # Delete a previous comment when the issue has been resolved - - if: ${{ steps.lint_pr_title.outputs.error_message == null }} - uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 - with: - header: pr-title-lint-error - delete: true - - label: - needs: [ validate ] - runs-on: ubuntu-latest - name: Add labels - permissions: - contents: read - pull-requests: write - issues: write - steps: - - name: Checkout your code - uses: actions/checkout@v4 - - - uses: srvaroa/labeler@v1 - with: - config_path: .github/labeler.yml - fail_on_error: true - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" \ No newline at end of file diff --git a/.github/workflows/base.yml b/.github/workflows/base.yml deleted file mode 100644 index 49c9334d..00000000 --- a/.github/workflows/base.yml +++ /dev/null @@ -1,135 +0,0 @@ -#Tabs not spaces, you moron :) - -name: CI for netris:base - -on: - pull_request: - paths: - - "base.Dockerfile" - - ".github/workflows/base.yml" - schedule: - - cron: 0 0 * * * # At the end of everyday - push: - branches: [main] - paths: - - "base.Dockerfile" - - ".github/workflows/base.yml" - tags: - - v*.*.* - release: - types: [created] - -# concurrency: -# group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }} -# cancel-in-progress: true - -env: - REGISTRY: ghcr.io - IMAGE_NAME: nestriness/nestri - BASE_TAG_PREFIX: base - -jobs: - build-docker-pr: - name: Build image on pr - runs-on: ubuntu-latest - if: ${{ github.event_name == 'pull_request' }} - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: base.Dockerfile - context: ./ - push: false - load: true - tags: netris:base - - build-docker-main: - name: Build image on merge - if: ${{github.ref == 'refs/heads/main'}} - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GIT_MASTER_TOKEN }} - - - name: Extract Container metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }} - # - #tag on release, and a nightly build for 'dev' - tags: | - type=raw,value=nightly,enable={{is_default_branch}} - type=ref,event=tag - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: base.Dockerfile - context: ./ - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - - build-docker-release: - name: Build image on release - if: ${{ github.event_name == 'release' }} - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GIT_MASTER_TOKEN }} - - - name: Extract Container metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }} - # - #tag on release, and a nightly build for 'dev' - tags: | - type=raw,value=nightly,enable={{is_default_branch}} - type=ref,event=tag - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: base.Dockerfile - context: ./ - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml deleted file mode 100644 index 4a8e3736..00000000 --- a/.github/workflows/labels.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: GH labels maintainer - -on: - push: - branches: - - 'main' - paths: - - '.github/labels.yml' - - '.github/workflows/labels.yml' - pull_request: - paths: - - '.github/labels.yml' - - '.github/workflows/labels.yml' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - labeler: - runs-on: ubuntu-latest - steps: - - - name: Checkout - uses: actions/checkout@v4 - - - name: Run Labeler - uses: crazy-max/ghaction-github-labeler@v5 - env: - GITHUB_TOKEN: "${{ secrets.GIT_MASTER_TOKEN }}" - with: - dry-run: ${{ github.event_name == 'pull_request' }} - github-token: ${{ secrets.GIT_MASTER_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/relay.yml b/.github/workflows/relay.yml deleted file mode 100644 index 72502a47..00000000 --- a/.github/workflows/relay.yml +++ /dev/null @@ -1,135 +0,0 @@ -#Tabs not spaces, you moron :) - -name: CI for netris:relay - -on: - pull_request: - paths: - - "relay.Dockerfile" - - ".github/workflows/relay.yml" - schedule: - - cron: 0 0 * * * # At the end of everyday - push: - branches: [main] - paths: - - "relay.Dockerfile" - - ".github/workflows/relay.yml" - tags: - - v*.*.* - release: - types: [created] - -# concurrency: -# group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }} -# cancel-in-progress: true - -env: - REGISTRY: ghcr.io - IMAGE_NAME: netrisdotme/netris - BASE_TAG_PREFIX: relay - -jobs: - build-docker-pr: - name: Build image on pr - runs-on: ubuntu-latest - if: ${{ github.event_name == 'pull_request' }} - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: relay.Dockerfile - context: ./ - push: false - load: true - tags: netris:relay - - build-docker-main: - name: Build image on merge - if: ${{github.ref == 'refs/heads/main'}} - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GIT_MASTER_TOKEN }} - - - name: Extract Container metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }} - # - #tag on release, and a nightly build for 'dev' - tags: | - type=raw,value=nightly,enable={{is_default_branch}} - type=ref,event=tag - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: relay.Dockerfile - context: ./ - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - - build-docker-release: - name: Build image on release - if: ${{ github.event_name == 'release' }} - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GIT_MASTER_TOKEN }} - - - name: Extract Container metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }} - # - #tag on release, and a nightly build for 'dev' - tags: | - type=raw,value=nightly,enable={{is_default_branch}} - type=ref,event=tag - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: relay.Dockerfile - context: ./ - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml deleted file mode 100644 index 0052dc64..00000000 --- a/.github/workflows/release-drafter.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Release drafter - -on: - push: - branches: [ main ] - -jobs: - update_release_draft: - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - steps: - - uses: release-drafter/release-drafter@v6 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/server.yml b/.github/workflows/server.yml deleted file mode 100644 index f81b23b9..00000000 --- a/.github/workflows/server.yml +++ /dev/null @@ -1,137 +0,0 @@ -#Tabs not spaces, you moron :) - -name: CI for netris:server - -on: - pull_request: - paths: - - "server.Dockerfile" - - ".scripts/**" - - ".github/workflows/server.yml" - schedule: - - cron: 0 0 * * * # At the end of everyday - push: - branches: [main] - paths: - - "server.Dockerfile" - - ".scripts/**" - - ".github/workflows/server.yml" - tags: - - v*.*.* - release: - types: [created] - -env: - REGISTRY: ghcr.io - IMAGE_NAME: nestriness/nestri - BASE_TAG_PREFIX: server - -# concurrency: -# group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }} -# cancel-in-progress: true - -jobs: - build-docker-pr: - name: Build image on pr - runs-on: ubuntu-latest - if: ${{ github.event_name == 'pull_request' }} - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: server.Dockerfile - context: ./ - push: false - load: true - tags: netris:server - - build-docker-main: - name: Build image on merge to main - if: ${{github.ref == 'refs/heads/main'}} - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GIT_MASTER_TOKEN }} - - - name: Extract Container metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }} - # - #tag on release, and a nightly build for 'dev' - tags: | - type=raw,value=nightly,enable={{is_default_branch}} - type=ref,event=tag - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: server.Dockerfile - context: ./ - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - - build-docker-release: - name: Build image on release - if: ${{ github.event_name == 'release' }} - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GIT_MASTER_TOKEN }} - - - name: Extract Container metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }} - # - #tag on release, and a nightly build for 'dev' - tags: | - type=raw,value=nightly,enable={{is_default_branch}} - type=ref,event=tag - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: server.Dockerfile - context: ./ - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/.github/workflows/warp-input.yml b/.github/workflows/warp-input.yml deleted file mode 100644 index 2edbaf4d..00000000 --- a/.github/workflows/warp-input.yml +++ /dev/null @@ -1,236 +0,0 @@ -#Tabs not spaces, you moron :) - -name: CI for netris:warp-input - -on: - pull_request: - paths: - - "warp-input.Dockerfile" - - ".github/workflows/warp-input.yml" - - "bin/input/**" - schedule: - - cron: 0 0 * * * # At the end of everyday - workflow_dispatch: - push: - branches: [main] - paths: - - "warp-input.Dockerfile" - - ".github/workflows/warp-input.yml" - - "bin/input/**" - tags: - - v*.*.* - release: - types: [published, created] - -env: - REGISTRY: ghcr.io - IMAGE_NAME: nestriness/nestri - BASE_TAG_PREFIX: warp-input - -# concurrency: -# group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }} -# cancel-in-progress: true - -jobs: - build-docker-pr: - name: Build image on pr - runs-on: ubuntu-latest - if: ${{ github.event_name == 'pull_request' }} - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: warp-input.Dockerfile - context: ./ - push: false - load: true - tags: netris:warp-input - - build-docker-main: - name: Build image on merge to main - if: ${{github.ref == 'refs/heads/main'}} - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GIT_MASTER_TOKEN }} - - - name: Extract Container metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }} - # - #tag on release, and a nightly build for 'dev' - tags: | - type=raw,value=nightly,enable={{is_default_branch}} - type=ref,event=tag - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: warp-input.Dockerfile - context: ./ - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - - build-docker-release: - name: Build image on release - if: ${{ github.event_name == 'release' }} - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GIT_MASTER_TOKEN }} - - - name: Extract Container metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }} - # - #tag on release, and a nightly build for 'dev' - tags: | - type=raw,value=nightly,enable={{is_default_branch}} - type=ref,event=tag - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: warp-input.Dockerfile - context: ./ - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - - build-warp-input-release: - if: ${{ github.event_name == 'release' }} - defaults: - run: - working-directory: bin/input - strategy: - fail-fast: false - matrix: - settings: - - host: ubuntu-20.04 - target: x86_64-unknown-linux-gnu - bundles: appimage - asset_name: warp-input-ubuntu-amd64 - - host: windows-latest - target: x86_64-pc-windows-msvc - bundles: msi - asset_name: warp-input-windows-amd64 - # - host: macos-latest - # target: x86_64-apple-darwin - # bundles: dmg - # asset_name: warp-input-macos-amd64 - # - host: macos-latest - # target: aarch64-apple-darwin - # bundles: dmg - # asset_name: warp-input-macos-apple-silicon - # - host: ubuntu-20.04 - # target: x86_64-unknown-linux-musl - # - host: ubuntu-20.04 - # target: aarch64-unknown-linux-gnu - # - host: ubuntu-20.04 - # target: aarch64-unknown-linux-musl - # - host: ubuntu-20.04 - # target: armv7-unknown-linux-gnueabihf - name: Build warp-input on release - runs-on: ${{ matrix.settings.host }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install Rust - id: toolchain - uses: dtolnay/rust-toolchain@stable - with: - targets: ${{ matrix.settings.target }} - toolchain: stable - components: clippy, rustfmt - - - name: Cache Rust Dependencies - uses: Swatinem/rust-cache@v2 - with: - save-if: false - prefix-key: 'v0-rust-deps' - shared-key: ${{ matrix.settings.target }} - - - name: Cargo build - run: cargo build --target ${{ matrix.settings.target }} --release - - - name: Copy and rename artifacts (Linux) - if: ${{ matrix.settings.host == 'ubuntu-20.04' }} - run: | - cp target/${{ matrix.settings.target }}/release/warp-input ./warp-input - - - name: Copy and rename artifacts (Windows) - if: ${{ matrix.settings.host == 'windows-latest' }} - run: | - cp "target/${{ matrix.settings.target }}/release/warp-input.exe" ./warp-input.exe - - - name: Copy and rename artifacts (macOS) - if: ${{ matrix.settings.host == 'macos-latest' }} - run: | - cp target/${{ matrix.settings.target }}/release/warp-input ./warp-input - - - name: Publish release for (${{ matrix.settings.host }}) - if: ${{ matrix.settings.host == 'windows-latest' }} - uses: svenstaro/upload-release-action@2.9.0 - with: - repo_token: ${{ secrets.GIT_MASTER_TOKEN }} - file: ./bin/input/warp-input.exe - asset_name: ${{ matrix.settings.asset_name }} - tag: ${{ github.ref }} - - - name: Publish release for (${{ matrix.settings.host }}) - if: ${{ matrix.settings.host != 'windows-latest' }} - uses: svenstaro/upload-release-action@2.9.0 - with: - repo_token: ${{ secrets.GIT_MASTER_TOKEN }} - file: ./bin/input/warp-input - asset_name: ${{ matrix.settings.asset_name }} - tag: ${{ github.ref }} \ No newline at end of file diff --git a/.github/workflows/warp.yml b/.github/workflows/warp.yml deleted file mode 100644 index eef598fb..00000000 --- a/.github/workflows/warp.yml +++ /dev/null @@ -1,234 +0,0 @@ -#Tabs not spaces, you moron :) - -name: CI for netris:warp - -on: - pull_request: - paths: - - "warp.Dockerfile" - - ".github/workflows/warp.yml" - schedule: - - cron: 0 0 * * * # At the end of everyday - workflow_dispatch: - push: - branches: [main] - paths: - - "warp.Dockerfile" - - ".github/workflows/warp.yml" - tags: - - v*.*.* - release: - types: [published, created] - -env: - REGISTRY: ghcr.io - IMAGE_NAME: nestriness/nestri - BASE_TAG_PREFIX: warp - -# concurrency: -# group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }} -# cancel-in-progress: true - -jobs: - build-docker-pr: - name: Build image on pr - runs-on: ubuntu-latest - if: ${{ github.event_name == 'pull_request' }} - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: warp.Dockerfile - context: ./ - push: false - load: true - tags: netris:warp - - build-docker-main: - name: Build image on merge to main - if: ${{github.ref == 'refs/heads/main'}} - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GIT_MASTER_TOKEN }} - - - name: Extract Container metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }} - # - #tag on release, and a nightly build for 'dev' - tags: | - type=raw,value=nightly,enable={{is_default_branch}} - type=ref,event=tag - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: warp.Dockerfile - context: ./ - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - - build-docker-release: - name: Build image on release - if: ${{ github.event_name == 'release' }} - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - - name: Checkout repo - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Log into registry ${{ env.REGISTRY }} - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GIT_MASTER_TOKEN }} - - - name: Extract Container metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ env.BASE_TAG_PREFIX }} - # - #tag on release, and a nightly build for 'dev' - tags: | - type=raw,value=nightly,enable={{is_default_branch}} - type=ref,event=tag - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - file: warp.Dockerfile - context: ./ - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - - build-warp-release: - if: ${{ github.event_name == 'release' }} - defaults: - run: - working-directory: moq-server - strategy: - fail-fast: false - matrix: - settings: - - host: ubuntu-20.04 - target: x86_64-unknown-linux-gnu - bundles: appimage - asset_name: warp-ubuntu-amd64 - - host: windows-latest - target: x86_64-pc-windows-msvc - bundles: msi - asset_name: warp-windows-amd64 - - host: macos-latest - target: x86_64-apple-darwin - bundles: dmg - asset_name: warp-macos-amd64 - - host: macos-latest - target: aarch64-apple-darwin - bundles: dmg - asset_name: warp-macos-apple-silicon - # - host: ubuntu-20.04 - # target: x86_64-unknown-linux-musl - # - host: ubuntu-20.04 - # target: aarch64-unknown-linux-gnu - # - host: ubuntu-20.04 - # target: aarch64-unknown-linux-musl - # - host: ubuntu-20.04 - # target: armv7-unknown-linux-gnueabihf - name: Build warp on release - runs-on: ${{ matrix.settings.host }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install Rust - id: toolchain - uses: dtolnay/rust-toolchain@stable - with: - targets: ${{ matrix.settings.target }} - toolchain: stable - components: clippy, rustfmt - - - name: Cache Rust Dependencies - uses: Swatinem/rust-cache@v2 - with: - save-if: false - prefix-key: 'v0-rust-deps' - shared-key: ${{ matrix.settings.target }} - - - name: Cargo build - run: cargo build --target ${{ matrix.settings.target }} --manifest-path ./moq-pub/Cargo.toml --release - - - name: Copy and rename artifacts (Linux) - if: ${{ matrix.settings.host == 'ubuntu-20.04' }} - run: | - cp target/${{ matrix.settings.target }}/release/moq-pub ./warp - - - name: Copy and rename artifacts (Windows) - if: ${{ matrix.settings.host == 'windows-latest' }} - run: | - cp "target/${{ matrix.settings.target }}/release/moq-pub.exe" ./warp.exe - - - name: Copy and rename artifacts (macOS) - if: ${{ matrix.settings.host == 'macos-latest' }} - run: | - cp target/${{ matrix.settings.target }}/release/moq-pub ./warp - - - name: Publish release for (${{ matrix.settings.host }}) - if: ${{ matrix.settings.host == 'windows-latest' }} - uses: svenstaro/upload-release-action@2.9.0 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ./moq-server/warp.exe - asset_name: ${{ matrix.settings.asset_name }} - tag: ${{ github.ref }} - - - name: Publish release for (${{ matrix.settings.host }}) - if: ${{ matrix.settings.host != 'windows-latest' }} - uses: svenstaro/upload-release-action@2.9.0 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ./moq-server/warp - asset_name: ${{ matrix.settings.asset_name }} - tag: ${{ github.ref }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..96fab4fe --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# Dependencies +node_modules +.pnp +.pnp.js + +# Local env files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Testing +coverage + +# Turbo +.turbo + +# Vercel +.vercel + +# Build Outputs +.next/ +out/ +build +dist + + +# Debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Misc +.DS_Store +*.pem diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 43a4f529..00000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "moq-server"] - path = moq-server - url = https://github.com/kixelated/moq-rs -[submodule "cli"] - path = cli - url = https://github.com/nestriness/cli.git diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..e69de29b diff --git a/.scripts/entrypoint.sh b/.scripts/entrypoint.sh deleted file mode 100644 index 1204469e..00000000 --- a/.scripts/entrypoint.sh +++ /dev/null @@ -1,285 +0,0 @@ -#!/bin/bash -e -trap "echo TRAPed signal" HUP INT QUIT TERM - -# Include our gpu helper functions -source /etc/gpu_helpers.sh - -# Create and modify permissions of XDG_RUNTIME_DIR -sudo -u nestri mkdir -pm700 /tmp/runtime-1000 -sudo chown nestri:nestri /tmp/runtime-1000 -sudo -u nestri chmod 700 /tmp/runtime-1000 -# Make user directory owned by the user in case it is not -sudo chown nestri:nestri /home/nestri || sudo chown nestri:nestri /home/nestri/* || { echo "$(date +"[%Y-%m-%d %H:%M:%S]") Failed to change user directory permissions. There may be permission issues."; } - -#Input devices ownable by our default user -export REQUIRED_DEVICES=${REQUIRED_DEVICES:-/dev/uinput /dev/input/event*} - -declare -A group_map - -for dev in $REQUIRED_DEVICES; do - if [ -e "$dev" ]; then - dev_group=$(stat -c "%G" "$dev") - dev_gid=$(stat -c "%g" "$dev") - - if [ "$dev_group" = "UNKNOWN" ]; then - new_name="nestri-gid-$dev_gid" - # We only have a GID for this group; create a named group for it - # this isn't 100% necessary but it prevents some useless noise in the console - sudo groupadd -g "$dev_gid" "$new_name" - group_map[$new_name]=1 - else - # the group already exists; just add it to the list - group_map[$dev_group]=1 - fi - - # is this device read/writable by the group? if not, make it so - if [ "$(stat -c "%a" "$dev" | cut -c2)" -lt 6 ]; then - sudo chmod g+rw "$dev" - fi - else - echo "$(date +"[%Y-%m-%d %H:%M:%S]") Path '$dev' is not present." - fi -done - -join_by() { local IFS="$1"; shift; echo "$*"; } - -groups=$(join_by "," "${!group_map[@]}") -if [ "$groups" != "" ]; then - echo "$(date +"[%Y-%m-%d %H:%M:%S]") Adding user '${USER}' to groups: $groups" - sudo usermod -a -G "$groups" "${USER}" -else - echo "$(date +"[%Y-%m-%d %H:%M:%S]") Not modifying user groups ($groups)" -fi - -# Remove directories to make sure the desktop environment starts -sudo rm -rf /tmp/.X* ~/.cache -# Change time zone from environment variable -sudo ln -snf "/usr/share/zoneinfo/$TZ" /etc/localtime && echo "$TZ" | sudo tee /etc/timezone >/dev/null -# Add gamescope directories to path -export PATH="${PATH:+${PATH}:}/usr/local/games:/usr/games" - -# This symbolic link enables running Xorg inside a container with `-sharevts` -sudo ln -snf /dev/ptmx /dev/tty7 -# Start DBus without systemd -sudo /etc/init.d/dbus start - -# Install Proton-GE for this user -nestri-proton -i - -# Allow starting Xorg from a pseudoterminal instead of strictly on a tty console -if [ ! -f "/etc/X11/Xwrapper.config" ]; then - echo -e "allowed_users=anybody\nneeds_root_rights=yes" | sudo tee /etc/X11/Xwrapper.config > /dev/null -fi -if grep -Fxq "allowed_users=console" /etc/X11/Xwrapper.config; then - sudo sed -i "s/allowed_users=console/allowed_users=anybody/;$ a needs_root_rights=yes" /etc/X11/Xwrapper.config -fi - -# Remove existing Xorg configuration -if [ -f "/etc/X11/xorg.conf" ]; then - sudo rm -f "/etc/X11/xorg.conf" -fi - -# Setting `VIDEO_PORT` to none disables RANDR/XRANDR, do not set this if using datacenter GPUs -if [ "${VIDEO_PORT,,}" = "none" ]; then - export CONNECTED_MONITOR="--use-display-device=None" -# The X server is otherwise deliberately set to a specific video port despite not being plugged to enable RANDR/XRANDR, monitor will display the screen if plugged to the specific port -else - export CONNECTED_MONITOR="--connected-monitor=${VIDEO_PORT}" -fi - -# A custom modeline should be generated because there is no monitor to fetch this information normally -custom_modeline="$(cvt -r "${SIZEW}" "${SIZEH}" "${REFRESH}" | sed -n 2p)" -custom_modeline_settings="$(echo "$custom_modeline" | sed 's/Modeline //')" -custom_modeline_identifier="$(echo "$custom_modeline_settings" | awk '{print $1}' | tr -d '"')" - -# Pre-populate GPU information manually -if ! check_and_populate_gpus; then - exit 1 -fi - -# Select the GPU based on user input or first one available -selected_gpu="${GPU_SELECTION,,:-}" -if [[ -z "$selected_gpu" ]]; then - selected_gpu="${gpu_map[0]}" # Select first available GPU - echo "No GPU selected, using first one available: $selected_gpu" -elif ! selected_gpu=$(check_selected_gpu "$selected_gpu"); then - exit 1 -fi - -# Print selected GPU information -echo "Selected GPU: $(print_gpu_info "$selected_gpu")" -echo "" - -# Get GPU vendor as separate variable -selected_gpu_vendor=$(get_gpu_vendor "$selected_gpu") -# Convert lshw gathered bus id into Xorg compatible one -xorg_bus_id=$(get_gpu_bus_xorg "$selected_gpu") - -# Get GPU card path if available -selected_gpu_card="" -gpu_screen_recorder_device_arg="" - -card_result=$(get_gpu_card "$selected_gpu") -if [[ $? -eq 0 && -n "$card_result" ]]; then - selected_gpu_card="$card_result" - gpu_screen_recorder_device_arg="-device $selected_gpu_card" -fi - -# Check if the selected GPU is an NVIDIA GPU -if [[ "${selected_gpu_vendor,,}" =~ "nvidia" ]]; then - echo "Selected GPU is NVIDIA. Handling NVIDIA-specific configuration..." - - # Install NVIDIA userspace driver components including X graphic libraries - if ! command -v nvidia-xconfig &> /dev/null; then - # Driver version is provided by the kernel through the container toolkit - export DRIVER_ARCH="$(dpkg --print-architecture | sed -e 's/arm64/aarch64/' -e 's/armhf/32bit-ARM/' -e 's/i.*86/x86/' -e 's/amd64/x86_64/' -e 's/unknown/x86_64/')" - export DRIVER_VERSION="$(head -n1 /dev/null -else - echo "Selected GPU is non-NVIDIA. Handling common configuration..." - - # We need permissions for the GPU(s) - sudo chown -R root:root /dev/dri/* - sudo chmod -R 777 /dev/dri/* - - # Create common config file - sudo touch /etc/X11/xorg.conf - config_common_xorg=" -Section \"ServerLayout\" - Identifier \"Layout0\" - Screen 0 \"Screen0\" - InputDevice \"Keyboard0\" \"CoreKeyboard\" - InputDevice \"Mouse0\" \"CorePointer\" -EndSection - -Section \"InputDevice\" - Identifier \"Mouse0\" - Driver \"mouse\" - Option \"Protocol\" \"auto\" - Option \"Device\" \"/dev/mouse\" - Option \"Emulate3Buttons\" \"no\" - Option \"ZAxisMapping\" \"4 5\" -EndSection - -Section \"InputDevice\" - Identifier \"Keyboard0\" - Driver \"kbd\" -EndSection - -Section \"Device\" - Identifier \"Device0\" - Driver \"modesetting\" - BusID \"$xorg_bus_id\" -EndSection - -Section \"Screen\" - Identifier \"Screen0\" - Device \"Device0\" - Option \"ModeDebug\" \"True\" -EndSection - -Section \"ServerFLags\" - Option \"AutoAddGPU\" \"off\" -EndSection -" - echo "$config_common_xorg" | sudo tee /etc/X11/xorg.conf > /dev/null -fi - -# Default display is :0 across the container -export DISPLAY=":0" -# Run Xorg server with required extensions -/usr/bin/Xorg vt7 -noreset -novtswitch -sharevts -dpi "${DPI}" -fakescreenfps "${REFRESH}" +extension "COMPOSITE" +extension "DAMAGE" +extension "GLX" +extension "RANDR" +extension "RENDER" +extension "MIT-SHM" +extension "XFIXES" +extension "XTEST" "${DISPLAY}" & - -# Wait for X11 to start -echo "Waiting for X socket" -until [ -S "/tmp/.X11-unix/X${DISPLAY/:/}" ]; do sleep 1; done -echo "X socket is ready" - -# Wait for X11 to start -echo "$(date +"[%Y-%m-%d %H:%M:%S]") Waiting for X socket" -until [ -S "/tmp/.X11-unix/X${DISPLAY/:/}" ]; do sleep 1; done -echo "$(date +"[%Y-%m-%d %H:%M:%S]") X socket is ready" - -# Additional non-NVIDIA configuration required -if [[ ! "${selected_gpu_vendor,,}" =~ "nvidia" ]]; then - # Get a list of all available outputs (connected or disconnected) - all_outputs=($(xrandr --query | awk '/ connected| disconnected/ {print $1}')) - - for selected_output in "${all_outputs[@]}"; do - # Create a unique mode identifier by appending the output name - unique_mode_identifier="${selected_output}-${custom_modeline_identifier}" - - # Create a unique modeline setting with the new identifier - unique_modeline_settings="$(echo "$custom_modeline_settings" | sed "s/$custom_modeline_identifier/$unique_mode_identifier/" | tr -d '"')" - - # Check if the mode already exists for this output (avoid duplicates) - if xrandr --query | grep "$selected_output" | grep -q "$unique_mode_identifier"; then - echo "Mode '$unique_mode_identifier' already exists for output '$selected_output', skipping.." - continue - fi - - # Add the new mode for the specific output (using the unique settings variable) - if xrandr --newmode $unique_modeline_settings; then - echo "Successfully added mode '$unique_mode_identifier' for output '$selected_output'" - - # Configure the output to use the new mode - if xrandr --addmode "$selected_output" "$unique_mode_identifier" && \ - xrandr --output "$selected_output" --primary --mode "$unique_mode_identifier"; then - echo "Successfully configured output '$selected_output' to use mode '$unique_mode_identifier'" - break - fi - fi - - echo "Failed to configure output '$selected_output' to use mode '$unique_mode_identifier', trying the next output.." - done - - if [[ "$selected_output" == "${all_outputs[-1]}" ]]; then - echo "Could not configure any output with the desired mode" - exit 1 - fi -fi - -# Make sure gpu-screen-recorder is owned by nestri -sudo chown nestri:nestri /usr/bin/gpu-screen-recorder - -if [[ -z "${SESSION_ID}" ]]; then - echo "$(date +"[%Y-%m-%d %H:%M:%S]") No stream name was found, did you forget to set the env variable NAME?" && exit 1 -else - /usr/bin/gpu-screen-recorder $gpu_screen_recorder_device_arg -v no -w screen -c flv -f "${REFRESH}" -a "$(pactl get-default-sink).monitor" | ffmpeg -hide_banner -v quiet -i pipe:0 -c copy -f mp4 -movflags empty_moov+frag_every_frame+separate_moof+omit_tfhd_offset - | /usr/bin/warp --name "${SESSION_ID}" https://fst.so:4443 & -fi - -openbox-session & - -# /usr/games/gamescope -- mangohud glxgears > /dev/null & - -echo "$(date +"[%Y-%m-%d %H:%M:%S]") Session Running. Press [Return] to exit." -read diff --git a/.scripts/proton b/.scripts/proton deleted file mode 100644 index 0ebad1ab..00000000 --- a/.scripts/proton +++ /dev/null @@ -1,610 +0,0 @@ -#!/bin/bash -# Version 1.1 -#Copied from https://github.com/noabody/unibuild/blob/master/data/wstart - -# WINEARCH win32/win64 not as good as binary wine/wine64" -# WINEDEBUG="-all" not as good as dev/null - -# complex command line builder to simplify command objects -# vars bin, pfx set to wine binary, prefix dirs initially -# menus will dynamically set bin pfx to specific targets -# menu shows gui based 32/64-bit pe header exe via pev -# var 'x' for unified cross-functionality -# ${@:2} skips 'wstart' and 1st arg - -# manjaro 21.2.1 -#sudo pacman -S bash binutils findutils gendesk grep icoutils pcre2 perl yay winetricks; yay -S pev - -# wnbin needs wine dir with: bin lib lib64 share -# manjaro default path is /usr -# can symlink various wnbin="$HOME/.local/opt" -#mkdir "$HOME"/.local/opt -#ln -sf /usr "$HOME"/.local/opt/wine -#ln -sf /usr/share/steam/compatibilitytools.d/proton-ge-custom/files "$HOME"/.local/opt/proton - -wnbin="/usr" -# top level wine dir, symlink into "$HOME/.local/opt" to flatten paths -wnpfx="$HOME" -# top level wine prefix dir -pntop="$HOME/.steam" -# top level linux steam dir -pnapp="$pntop/steam/steamapps" -# steamapps subdir -pnbin="$pnapp/common" -# proton subdir normally under top/app -pnpfx="$pnapp/compatdata" -# proton prefix subdir normally under top/app -pnpge="$pntop/root/compatibilitytools.d" -# proton ge -progs="drive_c/Program Files" -# Program Files standard subdir -stcmn="Steam/steamapps/common" -# windows steam client subdir under progs -desk="$HOME/Desktop" -# desktop entry folder -icon="applications-other" -# default icon -temp="$HOME/Downloads" -# temp folder -clprm=("${@:2}") -# store cmdline args minus first option -x="$(echo "$1" | grep -Pio '(?<=-)[wp]')" -# 1st letter of 1st cmd line arg determines wine/proton -if [[ -n "$x" ]]; then - xarg="$(echo "$1" | perl -pe 's/-[wp]/-x/gi')" -else - xarg="$(echo "$1" | perl -pe 's/-x+/-/gi')" -fi -# drop 1st letter x or change to it -xcmd=() -i_mnus=() -myprnt=() -i_syms=() -pmenu=("Command Prompt/wineconsole.exe" "Control Panel/control.exe" "Registry Editor/regedit.exe" "Task Manager/taskmgr.exe" "Windows Explorer/explorer.exe" "Wine Configuration/winecfg.exe") -# scalable built-in programs menu -unset WINEARCH WINEDLLPATH WINEPREFIX STEAM_COMPAT_CLIENT_INSTALL_PATH STEAM_COMPAT_DATA_PATH -# prevent shell inheritance of env vars we use - -w_menu () { -PS3="Please enter your choice: " -select answer in "${i_mnus[@]}"; do - for item in "${i_mnus[@]}"; do - if [[ $item == "$answer" ]]; then - break 2 - fi - done -done -# repeating menu requires valid selection from array -if [[ "$answer" = "quit" ]]; then -# pop quit from end of array for menu option - exit -else - xmrtn="$answer" -fi -unset i_mnus -clear -} - -# Path ordering: wine64 x64/x32 or wine32 x32 then standard -# Order critical to proper operation -xn64 () { -xstrt="wine64" -xnldl="$xnbin/lib64:$xnbin/lib" -xndll="$xnbin/lib64/wine:$xnbin/lib/wine" -} - -xn32 () { -xstrt="wine" -xnldl="$xnbin/lib" -xndll="$xnbin/lib/wine" -} - -xnint () { -if [[ "$x" = "p" ]]; then - xnbin="$pnbin" - xnpfx="$pnpfx" - dpth=(4 3) -else - xnbin="$wnbin" - xnpfx="$wnpfx" - dpth=(3 2) -fi -} - -xnexe () { -# menu installed wine/proton or exit -readarray -t i_mnus < <(find -L "$xnbin" -maxdepth "${dpth[0]}" -type f -iname 'wine' ! \( -ipath '*/sbin*' \) 2>/dev/null | perl -pe "s|\Q$xnbin\E/(.*)[/]*bin/wine|\1| ; s|/$||" | sort ; echo "quit") -if [[ ${#i_mnus[@]} -gt 2 ]]; then - clear - w_menu - xnbin="$(realpath "$xnbin/$xmrtn")" - unset xmrtn -elif [[ ${#i_mnus[@]} -eq 2 ]]; then - xnbin="$(realpath "$xnbin/${i_mnus[0]}")" -else - echo "No installed Wine/Proton found." - exit 1 -fi -} - -xndef () { -# create default prefix cross-function -if [[ "$x" = "p" ]]; then - if [[ ! -d "$xnpfx/0" ]]; then - # always create default 0 prefix - xnpfx="$xnpfx/0" - echo "Creating default prefix: $xnpfx" - mkdir -p "$xnpfx" - STEAM_COMPAT_DATA_PATH="$xnpfx" "${xnbin%/*}/proton" run > /dev/null 2>&1 & - xnpfx="$xnpfx/pfx" - fi -else - if [[ ! -d "$xnpfx/.wine" ]]; then - # always create default wine prefix - xnpfx="$xnpfx/.wine" - echo "Creating default prefix: $xnpfx" - WINEPREFIX="$xnpfx" "$xnbin"/bin/winecfg > /dev/null 2>&1 & - if [[ -d "$HOME/.wine" && "$HOME/.wine" != "$xnpfx" ]]; then - rm -rf "$HOME"/.wine - ln -sf "$xnpfx" "$HOME"/.wine - fi - fi -fi -} - -xnpre () { -# menu wine/proton prefix -readarray -t i_mnus < <(find "$xnpfx" -maxdepth "${dpth[1]}" -type f -iname 'system.reg' 2>/dev/null | perl -pe "s|\Q$xnpfx\E/(.*)/system.reg|\1|" | sort ; echo "quit") -# use perl escaped Q/E to preserve special characters in path variable -if [[ ${#i_mnus[@]} -gt 2 ]]; then -# display menu with min two options plus quit -# guard proton cross-function - if [[ "$x" = "p" ]]; then - # shellcheck disable=SC2044 - for value in $(find "$xnpfx" -maxdepth 1 -type d -ipath '*/[0-9]*' -printf "${xnpfx///compatdata}/appmanifest_%P.acf\n" 2>/dev/null); do - test -f "$value" && myprnt+=("$(grep -Pio '^\s+\"(appid|name)\"\s+\"(.*)\"' "$value" | perl -pe 's/.*appid.+?\"(.*)\"\v|.*name.+?\"(.*)\"/\1 \2/')") - done - if [[ ${#myprnt[@]} -gt 0 ]]; then - printf '%s\n' "${myprnt[@]}" | sort - fi - # correlate appmanifest to proton prefix and list before menu - unset myprnt - fi - w_menu - xnpfx="$xnpfx/$xmrtn" - unset xmrtn -elif [[ ${#i_mnus[@]} -eq 2 ]]; then -# don't menu if only one option plus quit - xnpfx="$xnpfx/${i_mnus[0]}" -fi -if [[ -d "$xnpfx/$progs (x86)" ]]; then - xn64 -else - xn32 -fi -} - -xnenv () { -# core env vars allow proper targetting of wine/proton -xpath="$xnbin/bin:$PATH" -xcmd=(env PATH="$xpath" WINEDLLPATH="$xndll" LD_LIBRARY_PATH="$xnldl" WINEPREFIX="$xnpfx") -# guard proton cross-function which adds on to core env vars -if [[ "$x" = "p" ]]; then - xcmd+=(STEAM_COMPAT_DATA_PATH="${xnpfx///pfx}" STEAM_COMPAT_CLIENT_INSTALL_PATH="$pntop") -fi -} - -xnldr () { -# loader default to proton as applicable, otherwise wine -if [[ "$x" = "p" ]]; then - read -r -p 'wine loader? [y/N] ' chse - if [[ "$chse" =~ ^([yY][eE][sS]|[yY])+$ ]]; then - xcmd+=("$xstrt") - else - xcmd+=("${xnbin%/*}/proton" "run") - fi -else - xcmd+=("$xstrt") -fi -} - -xnset () { -# set cross-fuction -xnint -# wine/proton menu -xnexe -# create default prefix as required -xndef -# wine/proton prefix -xnpre -# wine/proton env vars -xnenv -} - -xlnch () { -# cross-function command line launcher -if [[ -z "$dbg" ]]; then - ("${xcmd[@]}" > /dev/null 2>&1 &) -else - echo "${xcmd[@]}" - if [[ "$dbg" = "1" ]]; then - ("${xcmd[@]}" &) - elif [[ "$dbg" = "2" ]]; then - (WINEDEBUG="warn+all" "${xcmd[@]}" &) - fi -fi -# prepend cmd with dbg=1 to see command and default debug output -# dbg=2 to see command and all debug output, dbg=? for command only -} - -allexe () { -# unfiltered list of exe in specified path -if [[ -n "$(stat --file-system --format=%T "$(stat --format=%m "$pedir" 2>/dev/null)" 2>/dev/null | grep -Pio 'fuse')" ]]; then - readarray -t i_mnus < <(find "$pedir" -maxdepth 7 -type f -regextype posix-extended -iname '*.exe' 2>/dev/null | perl -pe "s|\Q$pedir\E/(.*)|\1|" | sort ; echo "quit") - # skip exe validity tests if file is on network drive -else - readarray -t i_mnus < <(env pedir="$pedir" find "$pedir" -maxdepth 7 -type f -regextype posix-extended -iname '*.exe' -exec sh -c '(readpe -h optional "$1" 2>/dev/null | grep -Piq '0x2.*gui') && (wrestool "$1" 2>/dev/null | grep -Piq 'type=icon') && echo "$1" 2>/dev/null | perl -pe "s|\Q$pedir\E/(.*)|\1|"' -- {} \; 2>/dev/null | sort ; echo "quit") - # perform exe validity tests if file is on local drive -fi -} - -fewexe () { -# filtered list of exe in standard paths -readarray -t i_mnus < <(env pedir="$pedir" find "$pedir" -maxdepth 7 -type f -regextype posix-extended ! \( -ipath '*cache*' -o -ipath '*/microsoft*' -o -ipath '*/windows*' -o -ipath '*/temp*' \) ! \( -iregex '.*(capture|clokspl|helper|iexplore|install|internal|kernel|[^ ]launcher|legacypm|overlay|proxy|redist|renderer|(crash|error)reporter|serv(er|ice)|setup|streaming|tutorial|unins|update).*' \) -iname '*.exe' -exec sh -c '(readpe -h optional "$1" 2>/dev/null | grep -Piq '0x2.*gui') && (wrestool "$1" 2>/dev/null | grep -Piq 'type=icon') && echo "$1" 2>/dev/null | perl -pe "s|\Q$pedir\E/(.*)|\1|"' -- {} \; 2>/dev/null | sort ; echo "quit") -# valid exe will have gui and icon -} - -alloth () { -# unfiltered list of variable type in specified path - readarray -t i_mnus < <(find "$pedir" -maxdepth 7 -type f -regextype posix-extended -iname "$xflt" 2>/dev/null | perl -pe "s|\Q$pedir\E/(.*)|\1|" | sort ; echo "quit") -} - -fewoth () { -# filtered list of variable type in standard paths -readarray -t i_mnus < <(env pedir="$pedir" find "$pedir" -maxdepth 7 -type f -regextype posix-extended ! \( -ipath '*cache*' -o -ipath '*/microsoft*' -o -ipath '*/windows*' -o -ipath '*/temp*' \) ! \( -iregex '.*(capture|clokspl|helper|iexplore|install|internal|kernel|[^ ]launcher|legacypm|overlay|proxy|redist|renderer|(crash|error)reporter|serv(er|ice)|setup|streaming|tutorial|unins|update).*' \) -iname "$xflt" 2>/dev/null | perl -pe "s|\Q$pedir\E/(.*)|\1|" | sort ; echo "quit") -} - -xbld () { -# cross-function custom prefix builder -pnpfx="$pnpfx/${clprm[0]}" -wnpfx="$wnpfx/${clprm[0]}" -xnint -if [[ -z "${clprm[0]}" ]]; then - echo "Wine/Proton prefix name required: (e.g. .wine, 0 )" -elif [[ -d "$xnpfx" ]]; then - echo "Wine/Proton Prefix exists: $xnpfx" -else - xnexe - echo "Creating Wine/Proton Prefix: ${clprm[0]}" - if [[ "$x" = "p" ]]; then - xnenv - mkdir -p "$xnpfx" - xcmd+=(STEAM_COMPAT_DATA_PATH="$xnpfx" "${xnbin%/*}/proton" "run") - else - read -r -p '32-bit only? [y/N] ' chse - if [[ "$chse" =~ ^([yY][eE][sS]|[yY])+$ ]]; then - xn32 - xnenv - xcmd+=(WINEARCH="win32" "$xstrt" "winecfg.exe") - else - xn64 - xnenv - xcmd+=(WINEARCH="win64" "$xstrt" "winecfg.exe") - fi - fi - xlnch -fi -} - -xpmn () { -# use specified exe, menu specified folder, or menu system -if [[ -f "${clprm[0]}" ]]; then -# parse 1st cmdline arg, queue if valid file - pedir="$(realpath "${clprm[0]}")" - xmrtn="$(basename "$pedir")" - pedir="$(dirname "$pedir")" -else - if [[ -d "${clprm[0]}" ]]; then - # parse 1st cmdline arg, use as path if valid - pedir="$(realpath "${clprm[0]}")" - test -z "$xflt" && allexe || alloth - else - # if no cmdline path, use prefix drive_c - pedir="$xnpfx/drive_c" - test -z "$xflt" && fewexe || fewoth - fi - # create menu, from path, of file - test ${#i_mnus[@]} -gt 1 && w_menu -fi -} - -xlyt() { -# pe layout for launch -# 64-bit prefix, 32-bit pe header, reset env to 32 -if [[ -n "$(readpe -h optional "$pedir/$xmrtn" 2>/dev/null | grep -Pi 'magic number.*0x10b')" && -d "$xnpfx/$progs (x86)" ]]; then - xn32 - xnenv -fi -xnldr -# if 1st arg is file/folder, skip it and run selection + remaining args -if [[ -e "${clprm[0]}" ]];then - xcmd+=("$pedir/$xmrtn" "${clprm[@]:1}") -else - xcmd+=("$pedir/$xmrtn" "${clprm[@]}") -fi -} - -xstm() { -if [[ "$x" = "p" ]]; then - sstrt="$(realpath "$(which steam)" 2>/dev/null)" -else - xnset - sstrt="$(find "$xnpfx/drive_c" -maxdepth 3 -iname 'steam.exe' 2>/dev/null)" - if [[ -f "$sstrt" ]]; then - pnapp="$(dirname "$sstrt")/steamapps" - xcmd+=("$xstrt") - fi -fi -# find wine/proton steam binary path, normally subdir of program files -if [[ -f "$sstrt" ]]; then - test -d "$pnapp" && readarray -t i_mnus < <(find "$pnapp" -maxdepth 1 -type f -iname 'appmanifest_*.acf' -exec grep -Pio '^\s+\"(appid|name)\"\s+\"(.*)\"' "{}" \; 2>/dev/null | perl -pe 's/.*appid.+?\"(.*)\"\v|.*name.+?\"(.*)\"/\1 \2/' | sort ; echo -e "steam\nquit") - # read appmanifests to create menu entries - test ${#i_mnus[@]} -gt 2 && w_menu && xmrtn="$(expr "$xmrtn" : '\([0-9]*\)')" - if [[ -n "$xmrtn" ]]; then - # lauch selection with steam - xcmd+=("$sstrt" "-no-browser" "-applaunch" "$xmrtn") - xlnch - else - # launch steam was selected - # minigamelist (short game list) for no-browser (disabled chrome) to save memory - xcmd+=("$sstrt" "-no-browser" "steam://open/minigameslist") - xlnch - fi -else - echo -e "Steam not found." -fi -} - -xpge () { - test -d "$pnpge" || mkdir -p "$pnpge" - test -d "$pnbin" || mkdir -p "$pnbin" - - if [[ ! -d "$(dirname "$pnpge")" ]]; then - echo -e "Could not create folder 'compatibilitytools.d/protonge' in:\n $(dirname "$pnpge")\n because that path does not exist.\nVerify script variable 'pnpge'" - elif [[ ! -d "$pnbin" ]]; then - echo -e "Could not create sym-link 'protonge' in:\n $pnbin\n because that path does not exist.\nVerify script variable 'pnbin'" - else - gedl="$(curl -sL https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases/latest | jq -r ".tag_name")" - # gedl="$(gh release list -R GloriousEggroll/proton-ge-custom -L 1 | grep -Pio '^ge[^ ]+')" - gever="$(echo "$gedl" | grep -Pio '(?<=ge-proton).*')" - if [[ -f "$pnpge/protonge/version" ]]; then - if [[ -z "$(grep -Pio "$gever" "$pnpge/protonge/version")" ]]; then - echo -e "Available Proton GE $gever differs from installed, updating...\n" - chse=y - else - echo -e "Available Proton GE $gever matches installed, nothing to do.\n" - fi - else - echo -e "Proton GE not found, installing...\n" - chse=y - fi - fi - if [[ -n "$chse" ]]; then - wget --progress=dot:giga "$(curl -s https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases/latest | grep browser_download_url | cut -d\" -f4 | grep .tar.gz)" -P "$temp"/ - - rm -rf "$pnpge/protonge" - tar -xf "$temp/$gedl".tar.gz -C "$pnpge/" - mv "$pnpge"/*roton* "$pnpge/protonge" - rm -f "$temp/$gedl".tar.gz - grep -Piq "$gever" "$pnpge/protonge/version" || perl -pi -e "s|(?<=ge-proton).*|$gever|gi" "$pnpge/protonge/version" - test -h "$pnbin/protonge" || ln -sf "$pnpge/protonge" "$pnbin" - fi -} - -xwn () { - export WINE_BRANCH=staging - sudo mkdir -pm755 /etc/apt/keyrings && sudo curl -fsSL -o /etc/apt/keyrings/winehq-archive.key "https://dl.winehq.org/wine-builds/winehq.key" \ - && sudo curl -fsSL -o "/etc/apt/sources.list.d/winehq-$(grep UBUNTU_CODENAME= /etc/os-release | cut -d= -f2 | tr -d '\"').sources" "https://dl.winehq.org/wine-builds/ubuntu/dists/$(grep UBUNTU_CODENAME= /etc/os-release | cut -d= -f2 | tr -d '\"')/winehq-$(grep UBUNTU_CODENAME= /etc/os-release | cut -d= -f2 | tr -d '\"').sources" \ - && sudo apt-get update && sudo apt-get install --install-recommends -y winehq-${WINE_BRANCH} \ - && sudo curl -fsSL -o /usr/bin/winetricks "https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks" \ - && sudo chmod 755 /usr/bin/winetricks \ - && sudo curl -fsSL -o /usr/share/bash-completion/completions/winetricks "https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks.bash-completion" -} - -usage() { - echo -e "\n$(basename $0): ERROR - $*" 1>&2 - echo -e "\nusage: $(basename $0)\n [-?a,--?add] [-?b,--?bld] [-?c,--?cmd] [-?d,--?dsk]\n [-?i,--?inf] [-?k,--?kil] [-?o,--?ovr] [-?p,--?prg]\n [-?s,--?stm] [-?t,--?trk] [-?u,--?cut] [-?v,--?ver]\n\n[?] = (p)roton, (w)ine\n (add) exe path to reg, (bld) build prefix,\n (cmd) prog menu, (dsk) desktop, (inf) exe info,\n (kil) kill wine, (ovr) overrides, (prg) exe list,\n (stm) steam, (trk) winetricks, (cut) shortcut,\n (ver) wine version\n" 1>&2 -} - -if [[ $# -lt 1 ]]; then - usage "one option required!" -else - case $xarg in - -xa|--xadd) - # cross-fuction path add to registry based on exe - xnint - xnpre - xpmn - if [[ -n "$xmrtn" ]]; then - ptadd="$(dirname "$pedir/$xmrtn")" - ptadd="z:${ptadd////\\\\}" - read -r -p 'prepend to system path? [y/N] ' chse - clear - if [[ "$chse" =~ ^([yY][eE][sS]|[yY])+$ ]]; then - xnreg='System\\CurrentControlSet\\Control\\Session Manager\\Environment' - xmrtn='system.reg' - else - xnreg='Environment' - xmrtn='user.reg' - fi - if [[ -z "$(pcre2grep -Mio "\[\Q${xnreg,,}\E\](?s).+?\"PATH\"=str\(2\):\".+?(?=\[.+?\])(?-s)" "$xnpfx/$xmrtn")" ]]; then - perl -0777 -pi -e "s|(\[\Q${xnreg,,}\E\](?s).+?#time=(?-s).*)(?s)(.+?)(?=\[.+?\])(?-s)|\\1\n\"PATH\"=str\(2\):\"${ptadd//\\/\\\\}\"\\2|gi" "$xnpfx/$xmrtn" - echo -e "$( (echo "$xnreg" | grep -Pioq '\\Environment') && echo 'HKLM\\' || echo 'HKCU\\')$xnreg:\n\n $ptadd\n\nPATH created successfully\n" - elif [[ -z "$(pcre2grep -Mio "\[\Q${xnreg,,}\E\](?s).+?\"PATH\"=str\(2\):\"(?-s).*\Q${ptadd,,}\E[\;\"](?s).+?(?=\[.+?\])(?-s)" "$xnpfx/$xmrtn")" ]]; then - perl -0777 -pi -e "s|(\[\Q${xnreg,,}\E\](?s).+?\"PATH\"=str\(2\):\")(?-s)(.*)(?s)(.+?)(?=\[.+?\])(?-s)|\\1${ptadd//\\/\\\\}\;\\2\\3|gi" "$xnpfx/$xmrtn" - echo -e "$( (echo "$xnreg" | grep -Pioq '\\Environment') && echo 'HKLM\\' || echo 'HKCU\\')$xnreg:\n\n $ptadd\n\nPATH added successfully\n" - else - echo -e "$( (echo "$xnreg" | grep -Pioq '\\Environment') && echo 'HKLM\\' || echo 'HKCU\\')$xnreg:\n\n $ptadd\n\nalready in PATH\n" - fi - # \Q \E adds \ to non alphanums but variable with \E ends \Q - # lowercase ${var,,} to avoid since path/reg not case sensitive - # linux path is case sensitive so user must not create duplicates - fi - ;; - -xb|--xbld) - # cross-function prefix builder - xbld - ;; - -xc|--xcmd) - # cross-function standard tools menu - xnset - readarray -t i_mnus < <(printf '%s\n' "${pmenu[@]}" | perl -pe 's|/.*||gi' ; echo "quit") - w_menu - xmrtn="$(printf '%s\n' "${pmenu[@]}" | grep -Pio "(?<=$xmrtn/).*")" - xnldr - if [[ -f "${clprm[0]}" ]];then - pedir="$(realpath "${clprm[0]}")" - cd "$(dirname "$pedir")" || exit - xcmd+=("$xmrtn" "$pedir" "${clprm[@]:1}") - else - xcmd+=("$xmrtn" "${clprm[@]}") - fi - xlnch - ;; - -xd|--xdsk) - # cross-function wine desktop - xnset - xnldr - xcmd+=("explorer.exe" "/desktop=shell,1024x768" "explorer.exe") - xlnch - ;; - -i|--install) - # proton ge - xpge - xwn - ;; - -xi|--xinf) - # cross-fuction program info - if [[ ! -f "${clprm[0]}" && ! -d "${clprm[0]}" ]]; then - # don't menu prefix on supplied file or folder - xnint - xnpre - fi - if [[ ! -f "${clprm[0]}" ]]; then - # offer to menu dll if no file given - read -r -p 'query dll? [y/N] ' chse - if [[ "$chse" =~ ^([yY][eE][sS]|[yY])+$ ]]; then - xflt="*.dll" - fi - fi - xpmn - IFS=$'\n' - if [[ -n "$xmrtn" ]]; then - if [[ $(readpe "$pedir/$xmrtn" 2>/dev/null) ]]; then - # if file is PE print 32/64-bit and dll references - myprnt+=("$(readpe -h optional "$pedir/$xmrtn" 2>/dev/null | grep -Piq 'PE32\+' && echo -e "FILE:\n$xmrtn\n \nPE HEADER:\n64-bit" || echo -e "FILE:\n$xmrtn\n \nPE HEADER:\n32-bit")") - myprnt+=("$(echo -e ' \nVersion:' ; peres -v "$pedir/$xmrtn" 2>/dev/null | grep -Pio '(?<=Product Version:).*' | tr -d ' ')") - # find dll references, filenames without spaces - myprnt+=("$(echo -e ' \nREFERENCES:' ; strings "$pedir/$xmrtn" | grep -Pio '[^<>:"/\\|?*\s]+\.dll' | perl -pe 's|([^/]*\Z)|lc($1)|e' | sort -u ; echo ' ')") - else - myprnt+=("$(echo -e ' \nNot a 32/64-bit program, no information to provide\n ')") - fi - else - myprnt+=("$(echo -e ' \nNo file found\n ')") - fi - printf '%s\n' "${myprnt[@]}" - unset IFS myprnt - ;; - -xk|--xkil) - # cross-function wine kill, must select same as running - xnset - xcmd+=("wineserver" "-k") - xlnch - ;; - -xo|--xovr) - # cross-function prefix override list - xnint - xnpre - read -r -p 'per application? [y/N] ' chse - clear - IFS=$'\n' - myprnt+=("$(echo -e "Prefix:\n$xnpfx\n ")") - if [[ "$chse" =~ ^([yY][eE][sS]|[yY])+$ ]]; then - # show existing per-application overrides - myprnt+=("$(echo -e 'Per-application overrides:' ; pcre2grep -Mio '\[\Qsoftware\\wine\\appdefaults\\\E[^\\]+\Q\\dlloverrides\E\](?s).+?(?=\[.+?\])(?-s)' "$xnpfx/user.reg" | grep -Pio '(?<=appdefaults..).*(?=..dlloverrides)|\".*\"' && echo ' ' || echo -e 'None found\n ')") - else - # show existing prefix overrides - myprnt+=("$(echo -e 'Global overrides:' ; pcre2grep -Mio '\[\Qsoftware\\wine\\dlloverrides\E\](?s).+?(?=\[.+?\])(?-s)' "$xnpfx/user.reg" | grep -Pio '\".*\"' && echo ' ' || echo -e 'None found\n ')") - fi - printf '%s\n' "${myprnt[@]}" - unset IFS myprnt - # A command like: - # perl -pi -e 's/(\".*msvc.*\"=\")(.*),(.*)(")/\1\3,\2\4/g' user.reg - # Swaps msvc entries (native,builtin) to (builtin,native) - ;; - -xr|--xrun) - # cross-fuction run program - 1st arg valid file to run, folder to menu, - # neither (sys menu), 2nd arg... passed to exe - xnset - xpmn - if [[ -n "$xmrtn" ]]; then - xlyt - # change to exe dir before run - cd "$(dirname "$pedir/$xmrtn")" || exit - xlnch - fi - ;; - -xs|--xstm) - # cross-function steam launcher - xstm - ;; - -xt|--xtrk) - # cross-function winetricks - xnset - # winetricks for selected wine/proton prefix - if [[ ${#clprm[@]} -gt 0 ]]; then - xcmd+=("winetricks" "${clprm[@]}") - dbg="1" - # use args if supplied, otherwise gui - else - xcmd+=("winetricks" "--gui") - # protontricks may work better - fi - xlnch - ;; - -xu|--xcut) - # cross-function desktop shortcut - if [[ -d "$desk" ]]; then - xnset - xpmn - if [[ -n "$xmrtn" ]]; then - xlyt - # change to desktop dir before create icon - cd "$desk" || exit - read -r -e -p $'Shortcut Name?\x0a' -i "$(basename "${xmrtn/.*}")" chse - # create desktop entry - gendesk -f -n --name="$chse" --comment='created by wstart' --custom='Keywords=wine;proton;launcher;' --exec="bash -c 'cd \"$(dirname "$pedir/$xmrtn")\" ; $(printf '"%s" ' "${xcmd[@]}")'" --icon="$icon" --terminal=false --categories='Emulator;Game' --startupnotify=false --pkgname="$chse" - chmod 755 "$chse".desktop - fi - else - echo -e "Invalid desktop location: $desk\nPlease edit the script" - fi - ;; - -xv|--xver) - # cross-function wine version - xnint - xnexe - xnenv - xcmd+=("wine" "--version") - ("${xcmd[@]}" &) - ;; - -h|--help) - echo -e "\n General usage: netris-proton -w? args\n -w? options for wine and -p? for proton.\n Type wstart by itself for command list.\n\n Edit script path variables as needed.\n bash, find, gendesk, grep, readpe,\n strings, winetricks, wrestool,\n pcre2grep, peres, perl needed by\n certain items.\n" - ;; - -*|\*|*) - # do_usage - usage "invalid option $1" - exit 1 - ;; - esac -fi \ No newline at end of file diff --git a/.scripts/startup.sh b/.scripts/startup.sh deleted file mode 100644 index 938326ac..00000000 --- a/.scripts/startup.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -#TODO: Fix the warp-input startup problem -if [ -z "$SESSION_ID" ]; then - echo "Error: SESSION_ID environment variable is not set." - exit 1 -fi - -#Open udp port to listed for QUIC events on -export PORT=${PORT:-"8080"} - -sudo -E /usr/bin/supervisord -c /etc/supervisord.conf diff --git a/.scripts/supervisord.conf b/.scripts/supervisord.conf deleted file mode 100644 index 7fbc50ae..00000000 --- a/.scripts/supervisord.conf +++ /dev/null @@ -1,42 +0,0 @@ -[supervisord] -user=nestri -nodaemon=true -loglevel=info -logfile=/tmp/supervisord.log -pidfile=/tmp/supervisord.pid - -[program:entrypoint] -command=/etc/entrypoint.sh -user=nestri -logfile=/tmp/entrypoint.log -pidfile=/tmp/entrypoint.pid -stopsignal=INT -autostart=true -autorestart=true -redirect_stderr=true -priority=1 - -[program:pulseaudio] -user=nestri -command=bash -c "until [ -S \"/tmp/.X11-unix/X${DISPLAY/:/}\" ]; do sleep 1; done; sudo /usr/bin/pulseaudio -k >/dev/null 2>&1 || sudo /usr/bin/pulseaudio --system --verbose --log-target=stderr --realtime=true --disallow-exit -L 'module-native-protocol-tcp auth-ip-acl=127.0.0.0/8 port=4713 auth-anonymous=1'" -environment=DISPLAY=":0" -logfile=/tmp/pulseaudio.log -pidfile=/tmp/pulseaudio.pid -stopsignal=INT -autostart=true -autorestart=true -redirect_stderr=true -priority=10 - -[program:warp-input] -command=bash -c "until [ -S \"/tmp/.X11-unix/X${DISPLAY/:/}\" ]; do sleep 1; done; /usr/bin/warp-input --namespace $SESSION_ID --bind '[::]:8080' https://fst.so:4443" -logfile=/tmp/warp-input.log -pidfile=/tmp/warp-input.pid -stopsignal=INT -user=nestri -environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY=":0",SESSION_ID="%(ENV_SESSION_ID)s" -autostart=true -autorestart=true -startretries=100 -redirect_stderr=true -priority=20 diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 37441bee..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "files.eol": "\n" -} \ No newline at end of file diff --git a/LICENSE b/LICENSE index 2c496e35..28403246 100644 --- a/LICENSE +++ b/LICENSE @@ -629,8 +629,8 @@ to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - Netris: The open-source cloud gaming platform for friends - Copyright (C) 2024 Wanjohi Ryan + Nestri: Your games, Your rules + Copyright (C) 2023 WanjohiRyan This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published diff --git a/README.md b/README.md index 349a0bf8..a2310fc2 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,24 @@
-
-
-
-
dAMQaiH zm^r3v#$Q#2T=X>bsY#D%s!bhs^M9PMAcHbCc0FMHV{u-dwlL;a1eJ63v5U*?Q_8JO zT#50!RD619#j_Uf))0ooADz~*9&lN!bBDRUgE>Vud-i5ck%vT=r^yD*^?Mp@Q^v+V zG#-?gKlr}Eeqifb{|So?HM&g91P8|av8hQoCmQXkd?7wIJw b z_^v8bbg` SAn{I*4bH$u(RZ6*x UhuA~hc=8czK8SHEKTzSxgbwi~9(OqJB&gwb^l4+m`k*Q;_?>Y-APi1{k zAHQ)P)G)f|AyjSgcCFps)Fh6Bca*Xznq36!pV6Az&m{O8$wGFD? zY&O*3*J0;_EqM#jh6^gMQKpXV?#1?>$ml1xvh8nSN>-?H=V;nJIwB07YX$e6vLxH( zqYwQ>qxwR(i4f)DLd)-$P>T-no_c!LsN@)8`e;W@)-Hj0>nJ-}Kla4-ZdPJzI&Mce zv)V_j;(3ERN3_@I$N<^|4Lf`B;8n+bX@bHbcZTopEmDI*Jfl)-pFDvo6svPRoo@(x z);_{lY<;);XzT`dBFpRmGrr}z5u1=p C^ S-{ce6iXQlLGcItwJ^mZx{m$&DA_oEZ)B{_bYPq-HA zcH8WGoBG(aBU_j)vEy+_71T34@4dmSg!|M8Vf92Zj6WH7Q7t#OHQqWgFE3ARt+%!T z?oLovLVlnf?2c7pTc)~cc^($_8nyKwsN`RA-23ed3sdj(ys%pjjM+9JrctL;dy8a( z@en&CQmnV(()bu|Y%G1-4a(6x{aLytn$T-;(&{QIJB9vMox11U-1HpD@d(QkaJdEb zG{)+6Dos_L+O3NpWo^=gR?evp|CqEG?L&Ut#D*KLaRFOgOEK(Kq1@!EGcTfo+%A&I z=dLbB+d$u{sh?u)xP{PF8L%;YPPW53+@{>5W=Jt#wQpN;0_HYdw1{ksf_XhO4#2F= zyPx6Lx2<92L-;L5PD`zn6zwIH`Jk( $?Qw({erA$^bC;q33hv!d!>%wRhj# zal^hk+WGNg;rJtb-EB(?czvOM=H7dl=vblBwAv>}%1@{}mnpUznfq1cE^sgsL0*4I zJ##!*B?=vI_OEVis5o+_IwMIRrpQyT_Sq~ZU%oY7c5JMIADzpD!Upz9h@iWg_>>~j zOLS;wp^i$-E?4<_cp?RiS%Rd?i;f*mOz=~(&3lo<=@(nR!_Rqiprh@weZlL!t#NCc zO!QTcInq|%#>OVgobj{~ixEUec`E25zJ~*DofsQdzIa@5^nOXj2T;8O`l--(QyU ^$t?TGY^7#&FQ+2SS3B#qK*k3`ye?8jUYSajE5iBbJls75CCc(m3dk{t?- zopcER9{Z?TC)mk~gpi^kbbu>b-+a{m#8-y2^p$ka4n60w;Sc2}HMf<8JUvh CL0B&Btk)T`ctE$*qNW8L$`7!r^9T+>=<=2qaq-;ll2{`{Rg zc5a0ZUI$oG&j-qVOuKa=*v4aY#IsoM+1|c4Z)<}lEDvy;5huB@1RJPquU2U*U-;gu z=En2m+qjBzR#DEJDO`WU)hdd{Vj%^0V*KoyZ|5lzV87&g_j~NCjwv0uQVqXOb*QrQ zy|Qn`hxx(58c 70$E;L(X0uZZ72M1!6oeg)(cdKO ze0gDaTz+ohR-#d)NbAH4x{I(21yjwvBQfmpLu$)|m{XolbgF!pmsqJ#D}(ylp6uC> z{bqtcI#hT#HW=wl7>p!38sKsJ`r8}lt-q%Keqy%u(xk=yiIJiUw6|5IvkS+#?JTBl z8H5(Q?l#wzazujH!8o>1xtn8#_w+397* _cy8!pQGP%K(Ga3pAjsaTbbXJlQF_+m+-UpUUent@xM zg%jqLUExj~o^vQ3Gl*>wh=_gOr2*|U64_iXb+-111a H}$TjeajM+I20xw(((>fej-@CIz4S1pi$(#}P7`4({6QS2CaQS4NPENDp>sAqD z$bH4KGzXGffkJ7R>V>)>tC)uax{UsN*dbeNC*v}#8Y#OWYwL4t$ePR?VTyIs!wea+ z5Urmc)X|^`MG~*dS6pGSbU+gPJoq*^a=_>$n4|P^w$sMBBy@f*Z^Jg6?n5?oId6f{ z$LW4M|4m502z0t7g<#Bx%X;9<=)smFolV&(V^(7Cv2-sxbxopQ!)*#ZRhTBpx1)Fc zNm1T%bONzv6@#|dz(w02AH8OXe>kQ#1FMCzO}2J_mST)+ExmBr9cva-@?;wnmWMOk z{3_~EX_xadgJGv&H@zK_8{(x84`}+c?oSBX*Ge3VdfTt&F}yCpFP?CpW+BE^cWY0^ zb&uBN!Ja3UzYHK-CTyA5=L zEMW{l3Usky#ly=7px648W31UNV@K)&Ub&zP1c7%)`{);I4b0Q<)B}3;NMG2JH=X$U zfIW4)4n9ZM`-yRj67I)YSLDK)qfUJ_ij}a#aZN~9EXrh8eZY2&=uY%2N0UFF7<~%M zsB8=erOWZ>Ct_#^tHZ|*q`H;A)5;ycw*I cmVxi8_0Xk}aJA^ath+E;xg!x+As(M#0=)3!NJR6H&9+zd#iP(m0PIW8$ z1Y^VX`>jm`W!=WpF*{ioM?C9`yOR>@0q=u7o>BP-eSHqCgMDj!2anwH?s%i2p+Q7D zzszIf5XJpE)IG4;d_(La-xenmF(tgAxK`Y4sQ}BSJEPs6N_U2vI{8=0C_F?@7<(G; zo$~G=8p+076G;`}>{MQ>t>7cm=zGtfbdDXm6||jUU|?X?CaE?(<6bKDYKeHlz}DA8 zXT={X=yp_R;HfJ9h%?eWvQ!dRgz&Su*JfNt!Wu>|XfU &68iRikRrHRW|ZxzRR^`eIGt zIeiDgVS>IeExKVRWW8-= A= yA`}`)ZkWBrZD`hpWIxBGkh&f#ijr449~m`j6{4jiJ*C!oVA8ZC?$1RM#K(_b zL9TW)kN*Y4%^-qPpMP7d4)o?Nk#>aoYHT(*g)qmRUb?**F@pnNiy6Fv9rEiUqD(^O zzyS?nBrX63BTRYduaG(0VVG2yJRe%o&rVrLjbxTaAFTd8s;<<@Qs>u(<193R8>}2_ zuwp{7;H2a*X7_jryzriZXMg?bTuegABb^87@SsKkr2)0Gyiax8KQWstw^v #ix45EVrcEhr>!NMhprl $InQMzjSFH54x5k9qHc`@9uKQzvL4ihcq{^B zPrVR=o_ic%Y>6&rMN)hTZsI7I<3&`#(nl+3y3ys9A~ &^=4?PL&nd8)`OfG#n zwAMN$1&>K++c{^|7< 4P=2y(B{jJsQ0a#U;HTo4ZmWZYvI{+s;Td{Yzem%0*k#)vjpB zia;J&>}ICate44SFYY3vEelqStQWFihx%^vQ@Do(sOy7yR2@WNv7Y9I^yL=nZr3mb zXKV5t@=?-Sk|b{XMhA7ZGB@2hqsx}4xwCW!in#C zI@}sc Zlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7 zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S zb+ 9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2 `1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc- zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I zgg{b=Iz}WkhCGj1M =hu4#Aw173YxIVbISaoc z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S( O(a0e7oY=E( zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/apps/docs/app/fonts/GeistMonoVF.woff b/apps/docs/app/fonts/GeistMonoVF.woff new file mode 100644 index 0000000000000000000000000000000000000000..f2ae185cbfd16946a534d819e9eb03924abbcc49 GIT binary patch literal 67864 zcmZsCV{|6X^LDby#!fc2?QCp28{4*X$D569+qP}vj&0lKKhN*HAKy9W>N!=Xdb(?> zQB^(TCNCxi0tx~G0t$@@g8bk8lJvX$|6bxEqGBK*H_sp-KYBnwz$0Q}BT2;- %I=)X2ub{=04r2*}TK5D+LXt~5{t z)Bof^+#0@Rw7=mKi|m$bX6 ?Bh~_rVfN!~Z5D+lYZ~eMdYd=)1 z?To(VG`{%|MBi{mhZ2~!F#vq`Pec9x)g^>91o^TxurUDvvGDqSS9st3-kw(m@3Xga z`qtIzyIr_nARq+I@sH7;0MG(2NPTSa#jh!1f4cEF5Xll)bpZ(>cyI|Q1wleT1wA5Y zq9^hv^x;~(?2G$>(CTL2)#Ou-rP=XDW$spn8<%0TH%F=^X^(F62Vd@bY`Wi$j$33w zf!U^8o_B|x>{pW$eFZG}b7#|uFueKt$`e9j!wHNBGQX67&nfgl(Ae`3qE-E+yBSfA zEnJSA6p%}|+P9ZIYR{w}nfaKIl V@b3YYzcH!?WNXRvg|J( z((lq^WAE%Q7;oE?zDk~Nvg1Dr_0)KH8m&HF%^&8bI!=#YAGqIx$Yf2lH9S*;=c=b6 zUHi?R*$?Q;>HU4-#?hGJ&dj2jq>d3;_NN_TeipMG!(E+ou)RL- kMQv(W$b9+k# z*%bh8;4)9Je-Giu+XwdbyoaSGei^KG*(1D)5+h{Kfg<`v)nU>dj}RiD_+VvZgb7>9 z-Qb^cdc0k1VSIW!onbm2*_uY*_+r1qe${8^DzXx MnX@F#u>I3_n0j_0ih#p?wd+ gPI5niQVbIIsk zkxy%JZZqLeb?p_DXdh1*9Z(O`Nm%TZ(zL`RA!dd+$VNO>qwecEt;dy5w%UK1@1exK zD~__{?4}pb@sGL5CjI=xAR7Jym_*l%fS~I(m>6873y~E7k;IfdA_0)|1$o9?h92Js zt4eu6$WMaSodkz#g|LB%Iw?^B?6x^A=arKjpBhhH6ZCbk2{;io5x)B3eh9R{KEOQX z9|&Q1T3-YGeF+9$doOBzU`TntM~LF~ON3aEZ|p9Y7+wF9qBi`6(hl}&)@-uZ`4zJl z>R`Cps(& x90dBZ~SLeCp?oa*PgM%P!bZaG*OS96bkBT*gF)q0a zxEd&4ZXn QHBuCrYm@m @ffPQTObP*2j+P z_?=gLxmGc32nceW5l5oy=+SB$=N%F^{g}lKR9(TljKIPHw)zVyZ?3ODUL^k;0CuW% z!;ErXcl6|m8OB+{5iYNEq}!Y@o<%r_^{5a($V)INcxkIcMA}Gd8LUShZK5U!u)=PR z6ZALS*{0F1Oxl?y$xE;JA+eyc6mW}LqFTZ3ZvVl#h*UFfj`$%JE0l8D!JRBYUlH!L zJ!uZs@&)nqNg9x8t`fZ?k4Ihgdv(Ogzr)|%{JQ|-g@#=7rCIq(Oo={zr!i7F_F!6; zqpKdMO={?6)e1SETQW+U?L?WPzQx9x#RrVu%xa5u$bDgLQrF-K4Iwd}9a=yS3(f1J z=&B1p=UwPU_#kfxrJ(YnDYZkc%{pp&sn{<~MdR_9^8y%u``RUJaJtY*yi=~R9ryu@ z9kzsKGwMLhZ1egl=e5m~k^Ft9pSfxI5B!$g1WaeqpO`4?C-3aj(gSm%1+@BdqpyAV z@X|;G-&|(jA;zG>T=$%}2gC%)gu@pTPQ)SpSw*2DuSrX((%PM=kQ&E@b=Ygy)lk zn6Q419734+(;{THjU2Uy9No0H4_jV1#6O)c>u@tbG6oWD;-8yHLnM^;;b@dWvle!?{40o`dO)$$EZ zM^@JN7b3@-+?UUO*P#gtLsy$!7gZcziDwAj59PsCAJm>m6r+l^X1z|%wu-jJhnQ&_ znPJwq9_*qBLoo*W`sPdYk10kPgf$aH@4qU~%&pFl2rZ0AHR*E-AvBR{F9QCehDa@z z95xXU{QZg|=zb2Pq36>@3je4inO+>S(`ht?)Z#zrHM(i>qE+>iU#!8v4QnWDruR08 zihT~ec3TRJh#llhgk(NqF04=VE8}61FWwvTi_}KWRnkIGbxQ)CAyBfBoVsTvRsR!v zeeHuptQ&5sDmg3vV_f9UtqYjdrR(_D^waATK``ZJjfZD5Kduvl1+l2-u6Qf=6Ombx z 7Sq ztJ92oU^LD6n$?=8G?#FGx#fF$d!2WBTf$UGVa}#`S@X&5dFIq%K!1Ikjs !+ybc~8&;<*f2$gyb>j{=&y@=kHsC%Xl#WTojY!)xQxm z+xUe-8Of9gTp&DDO h{Yy9#6leUk5m&-h{G7M@bsLtAJZq1|X(5;ulY z-D2nY-`lAFFZza${swOYsV>&wyw;MiiXw9Ze4so}{Flt`IeJQ5b1l1!d)yG4v?WEO zO3yg9oy--%g}hya8*T);IAWhS&T>>KL9Je(WS#9P#! $_f6!1`7cfKj*+i>@*tP8Mjj|un5Z`YGD>MiCU!adPX zx#5sU8_)@)5fHgRLdp7k;l9M r_8H3SOvpCBbBRGBQ`Wih*Xpj<)C6}E4SH?GeM1wt)HAM~N<~ejyt^Wpq0tmp z6X&e+wbKjOt@{1ng^s>(semrGFCQLXu|@O1tvtmYwuZ`$BSe{a-011Sk2a~(>MVE0 zpIQ7LpuG+o?lOHuw%e_kJ6yAoXCpu*QQeY%8SNh6?$89*3`>%=;EOJb+gtz&Kp|yv zfPV+nw`uTKbxE3vpT)v3C@L}V3(f*@_3N$Flc(8e<6F?hmPF|Dt%$W})5dMX(nql2 zOMy&yEWPokJ^l?odvVv&l(un4B`x0UHu6T8LraPoL*NltIUElZ5m!YVjcyZe{0Gtx zK{scl85IYuMO$EBG$tHHu0zc0wi&8rW3`d{VJC$oYNJ?m2MBStoGQ!4xQLHS_tBeI z4=tL^Lv>Bj^g79fzfCc?aTHu%Uvn6&+a@&*N~Rba)gbaLl?WBo%1^Pjx=t&|S^9nh zu(^m2A5XEp+ZN2L2#w^7IpLW%BW#F@6{50p0liwKYe!&NWu2F@oIV-5r<}*;+3|bP ze>zfTOAXqW760vNex|NG!Xz~@Wcd5UhOk&n5clNgylEGuS)lF7K$c{a+Hl#rx-2Ic zD(HhN(=Sa(v|zonLt 6q9;>ZBVh6n__yB8Pn7WCY*KX8V+u(@n9e zOTe7&?}Fvh 8wH RCgku@eEVodSv4NBH%wJEO4wEp#-}%%$wR$2D5JR|@$vRkRb7}iIhxv; zshP$6ckt<2KCd5K9#gwy%I*Ey>Fe20M_29Y=)g1AcBH#@^pXEtP30j`IbaZgR2{t^ z`r?E$A9Zdf@wct0$aRwJ=i9-^yxU 77e+%zOG9j-MXBP)nekE iIFHfS>Ba|3w;D?|dL35fhFX>Fi zQcepJ aiZvXu&=IsDUMoZIo?5N1`h|7?WDfbJmXcY~w_lg&|t|BlK!`YFCDcu*n(Sa{%c z4$vg-+drB`)#x8&q6x0pG5p+BKvfIu#O32<*&LF;z8q?zL`41|Yicx^Yq4jz6>WcO z4=~f8fF;F-A=fL28*f$mLyZ)0X>6z$b iG4VuDpiV4z zY~_evrt9XZfAzEyT`LtOtA^qKGM{Tq8NMHGIOL>T;4vaiE@lH-C<@aOeh_^m?<&&h zdXSPA^^n-i>Uj{Z%Lb+6v5B_zD^V _GWE1OBNlHndI9YW5kD^Kk@cZ&Ia z6oRdBan^1xma-m6+`d|wRJR`V ~A;L2zw&Yu_yoTtgzTrhi-xxFYK659im n;^%TR%3!4mYTU`we=`K-=!r$)M^U|fng0gd4 zY&D|@id)hQ6lZ6$q#} %snpqqb>@aUApp7;*W>0UoVkg(l}MYC6COXI29 zGc~J-gZ4vC{yy!bjlkXM?rF2de*R#dL=(PI9-L-quUxck&u`DmTQjI#p*2mPjNqc? z$X9XK{UtI;@pJUK?cwIxV;%;lTG0!%y 5 zJpWhb11vK@d2I=!;)F5vM`ML)^6b)LCj<7zlFm7!F$_T_`hyDZ>MEBe@A%a+9RG#y z_*KevIxJ(rEBNzd_KBWC<+$;IWH5}W4eTN}TM#4*`n;PelIth54aC}8|KHL1Kd9hY zdg6C1@KJ_+m6OHmY-}EB_QYaDnd8)^Y#fTGC1QB3E&Rq&s{PIUL5DzjJG<4E+;x=! zz3?hDSALlK#YF2II?cmMlq^D)riLWp(`LjFJNTY&BkIxb04C*yZ)Vjb*8{OJ&U(p# z3cxi}BFmgL+V%Ew9*g|D_V>-jj>E&_kXF}@LX&k)UuVIb+!>`~SGXZrZd9yBFoeR5 zNrxA*){}5*BIRJ3GSAb5CW!RX5}9`W*v3|J4v;znteT1Jn6BmRxF0|>v+o2A%ix3E z_}aH+5hk}2B`>5kW}hg%W`rkIVN-e8*j3!A(mQ&IFKdo(2cn%(!rGGG-la2y4dz)d z;cU;$Z5l<(tUS+pPC9~e+Sl_5OnGT=${=;{P%TayUQ^o1bm#Qel@0Ea2wDFsgpR8p z%{42-o*aWIGVFESm@;QGB)am8yb0`j>EazkuEVoKMd!r}nWzO!rg#7+BuCQ?4|TZ^ z`|;e56wJl>(SLl!DEUo1dvlUaqZZ{;%CQg!oaJ?FFxAmVK6uv$_; SHB!^)t!xv- f_$Bs$C)M jJg|HA#qe9b`BSwl8 z2McXH6Uvn|ClJyKV8|OT-V{LIG1v~h>gQprzhfK(DrmFQ4M!VgO!ZS8o6D1p%RSmV z+Xf5C09vC7w0t%eXb8L=U(~wlP)tZ3TaN #j4{NWJFL7# zMeiEPfaIS?IHAdP9aH+sm5udxfk^i!o76N(KewVyMk&0@OpX6rwAKG} 3?0IvE?(cPM;r3Az!_xLiYFY&)}Sl<19#fU0x zj-uZ}`Ey9BnVxqbj#D{R24|$jM(dNl2KH#FvbDSz*@x<{sy48Gz=(yRiYW`ofYMu+ zzdPsn^PhpxWX2v}!sahrD*o$$3k;XDHq|HQU^rDKHq%xw$IafF=^BmtY8T@#Z%YDW zAdx@ahu2vaLq%D&-me?D (}&)mEb|5m{{oc6#p!vRnXxn izH Wv)adXiBb>q0*jdBJ~Zv<2B}4vZ{P z>E)ayXwPyT&!MqX{ao=#mpGCX5|61&)PEQKmppcZigqM*Xe+;DOlb?AQ8hZ 8S0~w3)(nNAK)Iuc7rg zfIT}yB^fVpt`B3Pkl;fBY6u~2&%W5O{d;oadPW=tcE^D^C>VI_JPYukh@T fhQoWZeCJ5B$7I19W@q_TM0($TkNK 3wl)QIl3|@|1RCuW$X^KSG)YgdJf$ zD&q2EfNK5$ `W1XPc!pW_jn16RK(}y~T4kUY!;u`93tAJiu%lz7ol{&ur{Q zrA4yCFcU|gV0|>p_`D&ByZc`)DL+`Qqx8bmSv%J+qdQd*Y<;Klb{>?OW@XKPzqewj ztIkvI-K;Hlf@9cCVRdISFG4&ME?xbBnin*J=9sxZ+*CAN{PGnwwyeqzbU^u}JEz&U zujyQvjy%LMauULwp0$59k|Lxd4Icntq<^uQ3!iJ0*EJT#GqBhF5^zk{hkBT< zKNwtg4Y`s4lJ-1VzUy%1!)~>kypou8iu}HY$;B}2qhX>w`(0ya>5ndBmNHvwz@<@d z)_T3Arr!pCuZ?)(&jZ=LnXHsU&B)ifpJd12LpQF3x4*zCIMUlbov*YMkDIX`ZQ}#B zDEm7;2>6H|!x9eQMZTTQ#83yK07tV{aiGreb{XKo=?{!()DRH+$I-(B{q;fyyO2n) z-rGbBGoMjZLapRim!$3W&f}tbELYcO^N@9^$@oA{Fw|v>Jo^sP%|m`>OsVrmyd1`r z*_-ScUuU|lzR~%OHT$uyWNQuw)pj`yF@eLl^+;zNjqf~|6huSAAIGYnALff2fZP5> zz7ARH{>mIa^RkT@w4ZV!CXF( cDn9w9CcPN-d;=6xcKKM>?vd2tUshA!XM9hA9JplyPAlKHA3W}2f4;=EdS9$VRk zJd#7BDuS+qpm{NTo#0B*Oj{$Z2l2)5j>joob07T0UCp(y#jl_ioRJq7;CrcFZ;7+D ziT+n )gme?&`MZ8Q3URYd1 zUXO6*c;TeIhsi*l(c2?lau-s#yIh8Vm$bB PL kB24pwd6-v8=f_57U7s_X=;?ZMPX$=V+KD?D%h69Plxj z6s25MR;B`_3y$P%?|Wl%v9)a+)Xt1ovYG0-8ZEx;{wk%oGLr8D(F1mGIiIYKO7qIT zkyAXyb QE{@($=@kZpE5&n7R;k?&LuC|WbUG$$?mLATHDk-iOwVbXY!1z4~OSn zL9Iql5xuH}kpF|{#T-2i$=3HA7g2YTKZSXE!U$;^53~)* >eS`jehs0aZ z?~}w>o$4HP*axMt=ZuDj#B+$8z;s<~`^+`;?9euOJhNPximpeOXZLVk`?)op?#1LI zsEJ(3NA-`GoL{a>z!{Z>a*D$!ZnSUCRh F+h1{YrQx-{HFin8WzZefO{l z8cNaM;e7wxPv4B1qdM6*FoUE$-f@ij7
)Qn+%qi1X#m$C)|q*>heV z_F1E1;>jFo_X_SxU4z7K=dzD=a^~oL!C9SEV -!KD$# mnz60qM-#pJFWBjB{A91?@LxNGc9%0{4?@cU#Y7z;WB&(t+Ux8ij z{ywC~@RW4y=k@~>Rr8pTmb$u=7qLo2Vpes~6>g_ENtTY7^pVeIg!wVc`DUmbY|`3M z-R+tCPAunS>R|zng`6f_20?)pLm}bSq%ja@pW1*wXr=T!IW0oYP6_8+GG^?eKvEc| z0FC0qr5|LsL5JWpacSeAuHLx1qO#F6G*`!D4x6a;L#0WM=HD&Vnsp=Ye)1&&^=NgK z$R=p#49`^kf{*a{V%70)-|osKU4qK8u*Ee`n^}AVgiVqOGq`)`$~)h-UbZ_TpWn5) z4AU%KuIEO^Hr5rLcT?KcOFj<^6-E5p*F`RXe_*jNQ- <*{pcs{>ypy$kvv5&h_=hdL<+0wfo7i8Zr zN2QPM2zwaYFfOrCFU7(G*GymiiuOMU H#o1w-P5{_<`RmBx9=5gvC W1?z*U9M+@ATPF1Psy-Tq}n0&H9|(XuzmZW30{I#a|z_}fb*J@}$Os9qoBgJ+y# zL#8>}`N|}X{(N$J8f*=>O{m7)%z$pbzMS2$yb0xce}L`230N n-UPkBNZy?Asat0>M==4pw7^P*~|GtzfgB9oEz zSk=B0wEed=|Ip)4I}(ZDBYlprm6N!l&1a{)JCR@4 >nZ9els~Gu+`<5ezJ3A;{B3`Ck6-7#p ziFkA{?4$2BcHuw~sGfB+sGG>sgP(eW)M^H@39}u3uf^6H SPdw&q^1jxpusc>E1p9-Su?Z)!3+F+@GwHP~|a`e`o(nklU0c z$M)W3BB{3Wn$(JgntlTNAP(iL>=b;wqp`!xMfLpa7@%+oG3L2vFv0Yd{WYP^a(Nq8 z;2jw%*$3xNJbL7%aTo}j30ZXHpm9k0sVi_dl8xNyUxDA006-~CjL%1|Og^BvD;u`5 z8eUsPX>1Jry+fY`?0PYEo<6g2_UycjSnM=1^3)pT)`AiKgWBpcxjSg3%AirFd5eP* zjvhK=PEj=}3VEoUv38N5?p1FxcdB>$Mz7(sJzqFUM>lEr#N`oGv ZQdU_A z`K|dEXc~4j2p{1d#j?jW&BI$yC00u2CH5F#XOFeDJdb_wrIAZDw(D<$uoFNSLNQjK zmiC)`+pCCs75<1NJK7S?oxlh4Tt%Ivo^LVH@gw3D4)|DOKg<>hv+aNnO=o?qd) zBGw!;7ZuIzay6nnEQm`!NKyMPw{nUUXT~md>GPvp*Ji(};@O*%38?IVxSFTwda8h& z9P2K-lj+LZ< %5qMIw`qxMMTPc z%1Ih+=0rkm9R@ptoN^AtL$sNVqokbv6{Nq1?bg%!*-vI88&j7m`-g2-c|Su|XmJBx z42Uub_~d!tp@Fbl(y`29x`NFGQrL6X@8ZCx;)-D4k4cR9IoeQM*@nMU9Mcy3(NVPh zf_5O8k#(#Tw=kX}S;sXT-GpXIvnQowOrmasb{$NgKNzM^`;cBQ=W!Z=VMcOmH1-K5 z^bm4kEA0rO iCv@0Apn-2k&-3;*9MhJ?#( z5?H^ 2k%5!&3qybCk7+d3658c9fRy__w>T(QRzEr z6APC_Hl-})SqZ!%4*dsbIVE1#BJPv13iV6|Xed34s`O*jDYmyxsWFar_w}g$gsP -F@R z<>#H5`3B+f=oWr9JZTL7Z{APZfW5v-+aMO7e%ivNM-W#S?|Fvcyr?2@iI$Su+QJ(8 zq)JjtA!jdwfSsSQtWg8*n1W0cSx?;@IDH_LVuf6GBSq35qz-=rbdpafaqtpmaJkD6 z)FU4N`0$>ky=urSXvZ>Z5+CCcp%Qe6L{{t03OeZ+ zRCbk>BIWW0M0}3H@E=v2SKJ_R*ZIq!pRh-^0N+(eDiOZF+6xCZvte(X-r1bgx@pkv zyuQ{9&YI}0FuXVNd!Ap~T&FwUkgPRr@D4#DMnvJm1tLU6;X~EEviiyPcadF~p;X(( zPfbc8;^*!TCu>?d3D>G!=ToM}c5s~~nAt0=*7w(iu|XXp80WJwG}1joDxbSx$aAHK z_4SS%_W_33*4oH7igJ$!EPp1HV0E_tW<^(9NXO>(=o@os$07H+%tEmGFeU>MmLY06 zM#|ETy5I{ZDk;tjza2(WL4xUo)ATh)MsAvybn+I26<_Ht)DH2oGS; c^iFp z4=e6_4}OiZpR&2uo*f!1=h32V;?$GJj0|3JHsw|;xTovqX6j}6C`D5HN!C5e+*J7P zKF^L%n<_W(?l+=cLx(%qs`;Bp2y!0pTKzjaegZo4s`ypoU3=-CzI7%Qc0MjP+hvIs zvb;zY9!)RL06PHqC)}A{LHB%6N+xzQphj`@&{1BeOL{q2x78AOd_f7I+j_IvX+|Vn z;q+Ntq*~#0;rD1E65XF4;rnv1(&|XIxp1t$ep72{*Id~ItSweukLcT7ZA-LpPVd|} zI|J&@lEL%J**H(TRG(7%nGS6)l#a|*#lfUcUj($QIM!Fu1yHlZf|t(B?*%dvjr||y zmQG$R(Djjf#x&R_;KPYt+psuo(YjfvRY^YCepUr0KHi`K5E}HpQ}UVqa+|mpE`Q|< zdhU+Q^%%w9`tGj9BKCBPd)P{E&^~Nr7WBf7rUWVMq8{5g_b0ORy#>P_8@k~pp8sm` zAK8t57^DN6D~ln!mx3!7?RnjSQCppf;A@p`!|uysB)zWt0wEJ~NP^3@9h=eFIzj}u zLin3oX0!Gg7N*gAUQ-kEVRUF2Fm*1dw5V-Uda}wp?rS*;JB*a%d<;*zOP(|x(?XuX zT@q#!3@qgxWi@Lnx@t<=W4YNd1RE{H-DO3K!}#f@QS$BNWln5GJmy1GJa}{u+9e|K zO1U T>v>KSj}% z1ang#sQMe>iK-&XnHp09x5iB-ZOc{map*+J5@myMGiwFnRd*g&rOsi|J!C!Hu((A; zk{)gS&m|={yS~CZCVsN h)&>Us*fr V$UMqb^bB81yA;$E^JwPt9k4NS5IK(?4EDb^A?E^z_xMj%`kfHxeCO9B#{Q6c ztL=4VCp>ts_-;MHzD@d;1d8)z^Lxwb+ b;Za^}>>?(vDJ)dJ=Iw`O6{ zuC-%5D~vgwyL>QxiSK1c-}xkG{zTaJqlTx)N2nHZ+MvhzFK M(L`;XO2D1AhuiWvQ`?uM(s(Phi{U1pa_;IqwzwsmyrO{H3KvRCl7LMSLGWoUjP z$oo{WpJ<}lz@>{WL$!+Q<{hhlP|KdeGe`AZPv;w?o=@B?_3SHT1GjI4PEScrQyH8r zPDPoV{+#wyfE@$V?tuKORJ!R*uK4H84tF{_%-is=TMLf8!&|N1cAt|vc$_3U9X+bX z21!M&@Pr@ry9YoEg2S&IWRFo~(+%E2_Xr~IJZC(CXIR#Lx_2+XtScM&FJ>bgXf0FA zPfTyb_3(SA*w5%HLA_6fMi3xkGmXe{AahG1?v7F4Ylte+sgNx8yGLE6p?5b ;zPAG&fcXYZRYmHY~O|d)^ay%!^0=f^?4r>4fNSZd(zC^9ro6d;5Lq& zqu+6;__+p}fb*>b26D^6eI>l%CJ;+T`zM>Jr#}sMG7K%OC?p?w)hi5GGJ05ziOq|! z=x=f4L>vZjEx~HXe#at~R17>w2uJ$!_`)8{^Tc-jR#Hi?jt-prwCrGgGn#3hl24dm zldosg>kw^8#goKcCK=*+s7-U4()3lMoxjW=HnQ_wb_FGqw*!nN`=Q7pBfaSk?msx9 z4w(l2)N4*{gEFy=qg~fFvk7l)fU6LpQTCK@WSvf&0LmzTGANW1@7+QJ3`M+dc2Y8y zt^o_&Lq1iu@x#K_YX3BI(R#bD!1=5b(kTB~ViL`hpz<*}?a~GD5=9I1B{L1C4+Y!A zA*Ore{`=ZUFVl<2uCxSy(0t{=6&oGBQqKe^J}Y>^UK%$EpwlXMh~1Xy6&; h}VGTdcm4+@ESi z$Xo1_84wSsl~^tnvi^v)!MfQFLhjh3Ay~l%t5k;|Spz?SolNM9 aJ`XJ+rE?UGs%Ydbo$nb(!mkD|0>$yf2HhWp#)nthTOk*s)IOEU_qIB_MT}8Gv7w z)1iert?Vlq6I<_FNO628gDnvW)ha~1@FnX@JdNItDGO=wkA{|iNP-4H!meaW;A3nZ z*tb~SNjVUMvsZWpGORQw2MXO#j{Y%0y?P5g{}7J&J*BzZp3L|uwdx2Ppq%3F1EY>m zSL{U _Z_W>0&M^inR~kA<-my?xX;qSE7eM-kG>l%7BZ5mn^}%`$CBimAz{c$w(a%;?K4-_vd|h6H=}23A>@E z$ziyCWpieAcE+IVDsiV5^Dr}g5^v|%)Zh~w;uiM{jvo@DzuB7vpcATzIOvzJMkSIt zf26$!EdeSgg|6AiJ*vvTq+1hol{BA7%CN4P83r2@Gmb4!U~TS%DJqALJ@oDxrw{KV zzl@mD$SYoAB;sNOy?`=l4vMHD0iO4wDUDY4$EN2L3ng@)bsU^EZv5b$e3}Ewmj0W$ zGwaO3)M%7dm31}_8(ODTfo&ke!rs{EF#%p+z)O;GFw6Md@=BFP<78(Gb92!|#_5rx zIUId2V7&}LdjT8rMnpf(pkPWuO)k0vo5X+!E55DR^6&6q%s$++q;!;_q-vC3F_M4b z=gR_=C%tuW@`w`aK_{OFYZ`E$WhRj}ezCN(+F`Cp%uP7I-D0kY+|3B={b0ULsgi_5 z^_7K3#>9=Tpy%USwd7)uDGU`1jt;-9T9Z{7(GHK-BjMzSDdaEJrJ|( e19O7=axuiqvckscp64zgVR@{C^ck&^ER#d^@CMPOP)^kX( zvBciKadokDb*w>}3Yf$hgPs?wM^iGo{D8!nZOmF2Geaz!Z#H=kbC?2R(AY92O@8hC zZ9aXT7k0mUsL4-RG! BAO_;t3iI`KBfbxhjQ7 zE;Ou=mhw^wP%bG5sCx1Od@mvWIIS9S82b`Uff+*eb1*tC3mbqwfsNDC!?`lWaoCHb zEK)M5$ysY9F~81=s$x)3YKNzS$}(n_LQY@mSHh2G@bP?taR4NfT+$7Ykzuh+ogQl4 z^q$$^2ZB&A;qB(Ki2`9a2%e%j&<3O{K<;2o>N&ClpX;R=mq;M2xa%OMq^EhT`Er{N zWso(m2D#g%AIvd5;EJt}y#Ue{Y1YEqk*mK`GzGvuApSw#%V1SO?o>+OpM3~a*G|(k zT1ek`jR H@W8PboCmKYhoNq&VNN*NI8s81-U1K1&KfAe2MYhbbY~k zNxeYxvAEWJ#@xYUxwn)%p2xJdw~Zd3)l^xq?ERE+_hq@5Vtq Noo+hA`2E4xl4VA9j z<58n##BL}in6!*gpoQ+4W|_icS=XlN=T6gG`&D;0PE!9}oizRS9!o&0e?Q#uw54#z zi4Tl3c}EV2UkyJ11Ruk}HT5Q6lJO$AV58k?a3 22~4l@s*CRw9nS z>j%EC#ja3R5pUnuw#p0;V4zy%nR6WJo~H)`uAx;!0w7z5CeY{A2(anBn-I6syH*Qe z+%%=3LRx8zE+io$W`pUMC?~j4&VzK>*an#;@^^E>zeK3=XCK6;u9pp6rY22maPvLl z`z&ftU*4?Xpf%&s?A@LcY|-La|I2`^6(e%NX@~FT%g*;q+2P%?JK1yNOM=_W`azLU zv?5hzA00oO6k_rApf~mM&@J+%w_k<3yoLuQS9sH%GISt?oobE9yfUd;ke<2SPrHRU z)9$v_dU#qc?D&aG@9n(%3;oI@{x+*p0=M!i5?XU)S@t4yv&~}?oBj=#>FAI9K2yY- z)%@LA4Nx#dT-f~umG28ayK;YCt0Y1$5%6`7-2#SB3K=uJFp|GV1QAZRyEU>`Qmsm2 z&fx!s*q7P2Ek_1M)KZOXi|5bnf>I@&BAmD55@EIx$eQKCTM?btfx&8BHK1Y2tgkfg zyS>9(&d_G=g5Lh`^Y{U8iJ%Z8iCsK^^ZU<2R8>x1^Cr`Ow%}{^W(Z(Lj7!85c32TY zSX})fwa<3`c=nJ@deoQEe}^t}7q#v%Qp&EhbNX8QF73Kbicrl!e)MJSuLn*#9YzFu z8IBvPn#-rv%m_c2r5L1&?V**H_OCY3){>UhI{?5o6Luq^eaNy`VzVH=tgX*SB;p;u zXpnS9vfL>FBveRvCG8K(t|m@e#y7$8AMb7TcWJ2zpJ;ff+@j-f!M?Md{C%|N?EL=j zq7)69qnr9+(`pngdgxFb|JX~<$JFaqlwAK|H)JX!&f<+A_1usw1UbJSBjBiwDFS1_ zUkZhZB01EPAeBj6 Q&t2-d1GpIg z@vmFNf-Rlrte~+O!ehclveAU*))^3)xrKm2m@J&(F;67BpYFIdOKWuVGqY{Y;MLAm zYKcgz?DQ2szyOTX8-XDED*~~Y{5Pqje)Et)n2h(MK=^TB?SfVW>iBMA8Gs|eflsc% zy5s4YhYtd8h6iG6H}m(qj67mc+Vu^I*V;qr{mlJKjJgS*2v)1uM35IpQL%v|{(kH< zrs}>E6Uz)#b}aH2qXRbloOwx15YCG^)Xa3Igeb4KE4j(JH#%3Mn*yF(Bh~$1wEiQ_ zWpkxeyVL?*Q=yBJ$P5>EPaglkjsEBeI0F12nCY>t(OUy4uOkDL4@POv{b!wJw7laU z4}L1ASUHdyqOUnWBZ?_3n;&Cgh%BWL^SK4*$SmGDhw(DQWT8WQJzlR2{i%4r?bz7# znv`Puo^{6X3QCWnH-1xDO^e6`LW3*!x(#}UQYb^ $mg z`TrJUaUt75yl^1#r-{J4e^3cAl=I_Dr=>xwm 7Lg7C%(`TwY*BG#QR26>le0+ zSjA8Kpk{_9Y|)SEY2B|2Lv-Cl3gV+L#6O}c!&g65jJ@HknlYmzUS$?;sa(dF{aIy7 z=>r`$X{U0m5?@2P!cXZRoH >HH8_3W`dWy13 zce1IF^&L7{DkW(g+eI$1shczxU?#d?dON16jK6flt~Chm`~GAYEV57P{@Oe;9+#Oq zkxXR@C13kLs=fg@v!H1=+1R!=wr$(CZQFJ>w!N`! jUP6r#mw2MMX{-)F_Sgh&vcW zKE{vkxb2N=1XV@_rK%6?*bjC>#k`8`QL88_Dn?4u*vZML5knoj56%U-t0O0_fTM<# z@yL|l)s7tseqKE@4)zPbaLr5&?X}E4Ot8k>PY-VRIH%*kl_$W7(DFrMJqW(|$e|aj z<}Z}X&QMT1GGoQQxSiMf=_!b*(=4>4l#EcTp$czycI(KP4|gOnGO6L0eDozy$`iq7 z+jF{tG>&vUUYR{Kr%9Lla1L*V;2bn1ARfY9ekHvww86i!>4)o}QIaNG6vxwoJBfN& zTG^klmW8FkoO~!yLKNX`W0QJT@pn WPD={ zkDz;wyAkm}F^IwL#dxW_h}LWVc2CV}$_(NXmvU=bO)ZX+l$cV81cR}n0(X4LGVJf3 z?*69|d6rTpKAe^X@(o*wwl|!et)4$unl%-wC0oil(%97D^_P6jz`wT8$Y8Eex`Ri$ zLXK0kqAI<$(RB^aT&In;aa{9*fb^QA#6{ZM3kUoC4I9VH@~zddNKFi2!)|z0EboNE z{ia6Q1z_Y(3Y3Ly7U?{jIitwcPB?I2KkD#~_R13bhc1oA>E=UoNp-Rm^(^Z$3)D+M zBP+9fE^}*E+e~z!_m$WpyYO%_fki#~;DgZnT)#X|4zIP3;zCXlDq<`sXKAaI$LZQ} zyyr@+j|I!~63a@fS&NEj95t-RdUCfMVvVfzMYuT2H}=XOX8I`FmUKz^F>cjo!0k5Q zF?s$VdCpZVq9&~-PfUFk=~ekfUT!72%3sepTk&V6s?>ZsA#WXBWxBkf%zOn9l{e+T zyM|jKz1s1FBgTbu558xvCcama)nrIOB8fOXl%v)5WK^JSqX?#fTc~k 5;-d zh(_Pd@tFK?0~+T@Iz9|(X3b6@M??0LlC407c VDzsbbl6>4~eXM1-5VW>Ztk*qTzZ<=h~(g;x?UD>*TPzg327N_qACmOb5l z^ @;AHAh=}YglwU6tAbT6ApgiV*B~yXi)m!wUxg2!t8E~ zmiQ;$RIsLL$|H!HI~>8zo}XYOF3N>af&yprcg!_FIHf<+vv$RD{(%0TM>ZN<9x@MX z2+xwNd+uQ|Y`tn8I*GHUX+xEXotm(v{vvG1!!eN7`0KCReg1}Gii3Coe_4@=a;|NC znt+p)%$|a-rLke|+O;%oij#`fw}RyKW|eu;J9Ht{%7%L9JTpnrS2LjFSNIGp#)`I0 zXh`y^GS%fTg$ q!#{) zC3`wacCX0}bd!Jo(AKHbye4qa+h8gyvE}Kr|1G1cA8Jg2Nk+DBUvzl|ZyVEFx*kru zTI-lfYI+HKIaSr rZ6v0hvuMLKrJGX$8n je|F&>?Dary8wZ+8jGzV&@ zE-~nInmW6Ep9@1VT3YQjx0*UO=Ps1~wI5IAFxM6 <(mK4WENak8@3mY5GSKD66sm2*H*yma)O0?)7Br`1`KeHi86a#yotkjM!s%JhTraYdP+lfcCj4mpTL=a>KSHmtd)aGkvevTSKC{ud zobS+D7KMna$Q} BYHAA6dU@!Rr7)jPv=4DQ`XJXcb#cPuWh78?MNtQ73`71@!K(xT&k9 zMuP)~u =%IFwfGP$jrR`N|4C|9B;RpmzZ1AJYJfm=ly&Tp;D9d` zy*NdJYGnPL4-YR)-|D`r4~Hs5yT^a#x69-*Ix^236v77`Zro|dn&`rsO>J*}k1mP# z;tG1o*fw^5fy}5-p{{6wZE^jWBv*Kbr~+`8Ah>6*${yA%l`d9v`15!BIw9BVfYaC9 z<~*1=*RymuE#tINYfUvTv2dlN_=Eup{6)VHL4SfV(M7W7&`sLY^C6ReR9Rv7=@7%i zgP(+ZRY1XeZqZhR+7uz|f=*)v?ZxTy&A-mIS}jp#8r>)z4ulp9oV;^==msMFeh9?u zUe`TC8bqEaKErcGH^cO11Nr{wFX`Wvq{3OaWr(X$!p-So4Aa9tO`<#mS}lg5go-}G z7qL_={ySe4y)Q@36h~%XPegs65PFSnrTVATTK8e5b4)yPlCx|=sfx<-P|9pNg3T7% zSK{mNqa%XXT~v+Xv2puxdwC?4`ln9%?ClYeXt~8m2~?qnLW3Pub;*sxU 4>FJy48F-(=`E7>< zN~(g}>iSE|%k#1=;(wNx?MCj1CAHyk1B4v@j9CX0i%-9WKLkGfY5bk$gd)Ixi+r4d zb3YO1Sz_u0w`4&;oM++e9mWLCTiLZk`)Ol|#i{KF9(DA-NlJS6UX|Ut`=-Oi8NDV^ zkA3{f*A2gx)11?2#&w*QjYe^mxmT`#oF#FSD3jRV9oK-?R(R@_AoU@#6;UgLd2+2D z-KBSQ9etULXa8!;*1M!7`Q77ieY5#*?P|Mzu=^9$9@F3feϣ%UY8`RWp~V-U_7 zDSM&-@cv_g11tXxtR8hhSsvhbm}^TIbEA^ zez~Ise9A5xP83c_%z83NHI&u7X>Mt9`pnf9TVC8vDso9r$$%-f#fu6f @a*df)uo-Q_5os=ED| zcEe;FMSWSJ&ct}ag!R8s`bGUZ `f~{uR>BX_16UIZu3|HQ{An_9v zHp7)lLClDc62YY@VO}JkS_2kF)MYGEO;oHS%W;YuDSf29meyQ*kC&Q@D5 Y()UirbQ zeT^&uH7^72nS2!YD|zY#+SZO~YV!l{p=s^XHa8fe1Wr{Ir ~lt? z&T9&mFQ)1Obn6G9 RBhN4O5^az)h8(>R7Z`?G=z2B6om`t%6fF1Lre{m0c~K~0 zXZ`%Asz;D)&nPl8w^z!q(xW3qYNIS&^j=w1)?4pd)hsHQJu%L&>=IUNSr-?V@a<#y zTe$XUE|?}yQS@G4Hzyq}NAYok$^v;@M3G?#N~=Lk0A7LKEyo$`IGn`T`3c+&xhE&g zGUdOb(GqsDl}c<$s___$V9iP|P`$KE66Ka)!2y>Q0W!(Z1+ ^C&IwAD7-&RKDm zn@lTqPUJ4whnly4U#AuBOX0`y@9}=T_iKqGj)SrPBvyHgUX8{~ cQ&n$YZMhEYGih$;=(NLFnCA; zJ<{P6EViq3GdR@A0F*j71H;Z7rbk7w@|D5)fHG%I7z!A3i&zoOG}HN^4@2Y@zZPW8k#z-2^|-~Kx5rTa2PJ#IoVGbx9( zms$_6iSdGT;U0f^Fi(^HUqEObfHCxveHQQmm5N68!ya{NsbpQ!J&T!=K7H*BqwI3( z<(8F_S1t|R9X3GYtkqCkY%MCbUS*P0tD$w9$x6L;NSmOB={inXdS_%wItd~9g6P?q zbe5ls)xwWyqa@6o*JRj jFm*JXA3Z_f7BV2Q zr|8x;r2WS3q$)JNtkgct{V{eZW>(nSUAP3`gSGb@Ta068{O(62Mo>By3C4Fb0xq|f zF($svLG@T|?ZAQUbnm64 rqnxjz@vnk*h&!BzyCpfWGxn*q%`b!2z>QlqgEDaj{z0qttc?)(Dp;3e z(yy(@YjF6%)!PGZ32TFI_{e0?Tr)><@Nh}%lMmyo%EZs_SFe3u*|%^JhjHJ1XGXjI z``I;gHSp+U(PI(CA?ZoqXG6&?-|KFNIGgKWj|g#lmAvsh#qaePKkb)vfkVD7B!sBr ztwrDIu9PhVp@t9Ota(3qIW!E{Stq+;x1M+(GR!qB3mdmJ6EZTkf_M>gnYyV*G~{HY z916Bf_&5)i%wxFAr?Wy1r!~*FqLp^99NyPZ-4ZHUy`0AUEz%0+bKT6;SlXPy5^Tn9 zit~>w<74c@=Of=s&C`mfeNxu7BhA8zZ8aUPGKDEyrHnjrw?v_#{)nzNg>MHveY_6& zIahSkcjLb>)xyrl4^6X;NEoPI)mVS-Scfz&*j>UtsLUHUf3vOFe{VM$n}31R)1_Fa z4wRr_VWG*Hdy0v*FC?d$Ny$k{ruxs|=UgZ|Sy?quvZB$JfE;70t4l^6I!Tg}>eg_Y zhK81qii(yP9MQjwa+ZXOmOLc=wpjZZ^%-&YDc@d%&LQkEUp2PM-s@%<^j>Wd*zN{m z`uIvD`cp vh gNaqh?8!Rgu94tEplL>Qwr-K^bDvl+D{FmgJ(tCsl2)sp@ zO8+Z6RqvHilF0dRCY(_2%LY>mq<5f&S<@pZhp;K@gL)OlJ+wIoR9s4riQb7G*E(lM zT`eb%v_6o2fW3}!gLQdyB7{*2rErWtZ}2<$YTTn(CQ5@*lC)YA5dw-p!l1x?Fy_?9 z3leg;vQHW-#<5G;K_a7kIS|F5x2qAw4Sjry?}hr}BzXo5(-a}1Nc2lv-Ux=7dw_`8 zr#XGH9?Vo})J2ws+jH0iX=yh&74q$+tx ?E~Dm3uC#iso#%yxrgdwQ4sCaS#1Ba6qP@BDTTlWER; z_Nr?)h}&+X`Ml*kd?vj9KHR?7)+4QIjnxNdB$-4<7JHBLV%V%f75QVvg=?DA@P6oP z6|+Cm*j}NeBB0y|MVZI3d#*aVv3lH!Q7ug;bw0VX0C1mpTVDuBU-JlZ&L*CrEx~@g zvWYf!%l@HoTQc76+$Rpybh9IpMMRVsTga6ck4{C19$W_b-Af|r-k^#2-F(MyP}23< zJMWV1g}YafX{Z_Rw!3?-w2Q@oq1XAOMa^scf-SjkdSwG>qy_`I@4l?3=ytXtN6RU2 zRZ?CjbKpA1i}Nb`pyH@hS5vF0`s&TH$8A47t|iq@+0wI3nn-*7ob=)T!M(+ruye(< zEom9SCd#4heQ9Q{%npGh? 2m^nPetWYjy9zv4ia)CrBY?wNlG2o zo#y=B+)MHX17`SlMY?qZw;;hMoH1JbxC*NXfq=*3fcaLt)%B_ci+Z)ctA0~lZj7Ga z6vPCw82$QeeH~s2j~}m&FVF^B5Z#nSEA;WOmT~aU%`JChOSD#3x0<`7!@a5b^5klL zE{Z37&-828$DM=l8@bj!a;JCkT=(qSYNG~mYkT=r@32 ~P p9^&Xo0jSK~pHT?6)f?A*>9E846 baRamXh?Tkxg^BjK7qxaHX5Y=?%)&BTXb5Z *` A0_YR#@MG~i$G&mDiVqBUEQmb~ zT-b 4iN)tcawMQpfkx7NKEy1{U4Vn; zOn`N`SltDeICuwP!4I|f=KE&G=pA?A`qlH (c;DggP= Hm>jkJD-jK*C)#5xi`pESX`hO z)^AT71c;{_!-jQ+x%G$xqtk23#8vBfe!c#pI5j)(Ml$E{L-uq#7#P3Dj=X_A4S*3H znBlL^`de1} *(c$r2C$6jPAg-6!zeYxwbp@XvS>GY%obNhzgT{!V7`!tha) z-OVAEZ3n1vj2wN3s5_q~K0zKsWlI+qA)%XFSW#i>btv)AF5|UYK=>9Y<6WAGKhDm9 z>~TM~Vs#Y8lnF4USHyMiR4{8lyM^>Z)dfszO%?SH*J5wT-p#cJ8(>q7#3GzJM3d!F z)-Za@re5UMqQu?&n9LL_mJ&?!G}p(vhkYsK$*YuiBRNhjbc7<@KedR3oRvOw-kVSZ zvNJxHu<3gx+=T^c628Kyo3L^%6*UVHBMCbNS2_Jlr-!(Ngw;HidJPwcpmr&Bl;U59 zAB?_`@FD&}7<> qFe0pDef`=aa3O_%Rh`BLksk z1{srtza=8k86* =_O@dPgt9HG}|0hh)8OxMT0bAv-7S4Fb0 zkDTdD6%FGH%Ue}4h>u*^j8xB_GrG5#lle?4 ZT|>P~W#{+!GHsZ*!l_U6YuunTFV9Vtqf-CEsVDxn`5 _ zegWYFLHw{L|BwU&fdGMe0K@i!pl&e$0rj!O=1jNPZnS(7m~FJ!;{0j+xwhQ_1~U3a z05a}_tpl|I+UO&6fZzNz(^vM}Pl59UBL=z@EIP=wKXq5@hQb5vVDO@jfd;{P@VE}| z0xY~=(gD8 rGvaO%D4&jJXmxC?gP==rw>UIMnZNf={z4-^_zT*Ix}^-jB!2k zsR-f(%PW|#fZ&86H7muGRa1F6?9pIhm8d1o)(~P9%PpAKkYJU7&co?v^T_d|XN>#) z!3%Ovp#4Gk3#VVSKe7Ntf`SREr>Nwd-~$rz5UQg@HcIOd^R48sza~N%YRAc *PdML#BJHU% zJ4#DV4c^j`%%U_6meXa;{ 077Xkq-yUny?@_RH-3I0cN|8tC7J-Yl^_$Rx=_&M=_pvWW=AIentRL+haM^^M| z!TJ`luzS(QKo?tikn2H_8}V;H#e buMG_;kI2~LHZbhVRt6=mpZSrx`hmuKFx z3p~}OY ^Pl#R_&`Tvz(4^{RvRshVqw-X{)yH9 zEB6-L=j}?Bvia1BBkGmEU6oSnRJ0X5#9WAJ5!^$}`yjW`GO}i*_erGV6U72-gx>Mg zW9BMOQH5LzgXPRFBi|ThsvX!{k@({FMf7vMm_e4Kum+_J(dn)Lx?}A7A200KY_cH& zZ?wkfPkq{|_yzY9Mp{DUScVS29VmOGc7M+9)y?>8m5*ZX!DrXh%3k;_&I`f^Jz;aa zG6fxC5KR*@I8 v{~$+WUL|Ow zdm)QEgfm<=jDTes8x>}^Dn@G@!Z^BWn9Ycf*$dbtGkju9OV o@ zN9JtXndsN)ukmMZ%1Mg5TXE=SLrr7 d` zicE-1gC h69WSS7B=|11x~CP`}>r@j8`xaL>{Fy B{^fQ6J{djI=f^&&_Ni6`plZ3X^D3zfCZpN`I&8SBNX_9q)=j-Lf8 zYj3Tk$k~Cdm-m&_^Hkc^D`A`*;amMNkFK47Q+u?<4Y#Q_%qirCD5S5q7wGWybg1UW z$zq7iLKXIoVfZFiSM=*s=+hIaizoRvD#CpOAc7%+GWDghfOQ{tkn;%--4Rdsk7xQ1 zgN;yU_w@wG?XGduS}l@sWdStsu_z{6;wpta-!bKJ1NAzhaD3S(Z8t)%dEs)kE+ZJX zn8YzdzDArt7?Kv}*9<8pI<*d*u?4C%O?XObZYL18(V7*eHk@GU(b-JnjL1;83=vDO zb;;T{Zg#laRQT$Wg#f8g5vXrExuj*tA6dXNu?im;@qC!!En^%oGk<^`Y5@}S?vGnV zm-(nUVZCeBf=!wptO)3Hfz9gv<&t@Q067A9>=;Xr60 1f*wx}hVjrJs18=Pv$yWBLbvBXw>nybvCzqLC zIvrQL3rJLYh8-HK9rX@x*;aZ$M_Xqe$PWEobiHM zan!Ew`Cb1ABg@_`z-Ti_x(?)N#Fhiceb94=| zCK| AfQTYM6Amb+3f%HP z^V4u0z!4aj5*Yk9nldObupdW=d4v&@(TVAIU?{B2Hx}l~SJ>@fP_{27JOjnY%M8y! zFSIc9J%$(=7 `=%Z6NZr7BHnsLv&+2%b>kD-&{MgM;U5Wu%_=ludGG0P;EwJW zw(-;ih3{K>ko83AOA0DgEede`#!H=+2LCmb%Yh pN|7{bPt;+fcyrUuMIsZgGWq{iXfqPthbyUu9!)+ zJU47kLMuMCbn6s|E6}bu>(tIG0N>CJ@Q1Pr-g*MPj?{*DqyMSS{34WyvLz~O|1T(2 zL!vZgEsOg4iI8i%i@K`0YFUfAzVi_26` 4t4@Yc>Z|G;(e@^zj z$RazYfEor}cw|BSH0 p1sR9{H z5rKppn$OY{68FPYH>jflNo`1d5gH7I{M`SGey=+||IUHXQR9o|yI5~A4_rC(H ziNr(c;DY1}bfi`lQWhNvTivA%hIb~>UV>O*vs~WqJra`4%34)gQ6uu5Nrd}@kHYv9 zYLbh=uF#=k5vVROQ>1en6Dca%))vuV#c!4zxpn!=w5MsUA#AfLGdLllZ>os0SP!nK zGUf>;|Jv{1!@HI8m)2JoqbVhd({sx;Gc2P>wrloU#1#(d{Nas#BgdxI^s9)uBt)ia zj2)`u`D3HwLNo5h=+lDJ($hi 5Jsnrb*)+;tiWerf?GSdd)}TI|C^nUe1fMU zzfJl#(}0yS{m1j&l~1x4VgC#H{ygyC0zhBjy>E89|ET$zUp;$Yo_wD9rnt914vO=h z8n1c%Fg^%@8mg8@?$*t??Ha4AQyTA5H{7(vs4cN*@=O~5Pf3@p1hkz~1CXK?M93+i zBqXGkV^Z)=$^k*BWke}|h2YK>LY`dmskcsyQ)qfsTllME$jy-N(`S^_8bYftjv&7F z8Ads#u;?7ay*K~W7YjgFIz&}bM46)5{8eq*q3tkjjBQz9Tcgu9bLK6WQr5IK^k4On zw~f9~hp|WEiNtH `~g%s2WN=~vDA Xev}Q) o5k(7`1|7#$y#ymJcr$Sy=QryTHvc8)XBDW+kk z7<8p_$g1GU=lWAVB5ZXR!o^d@Hd8*Vj7 zic{OJUL zu*i!8;e3v#P+SpiNyT4P&D~X5{!z)^RZ;y>(YILzB1IicRfSYl*>y?Dc1clpNtwD? zO}kl #_f7G8LH@1RZ&~28Q1DGP z_%SQ&3;}K-54)z9MF>J-+OC5F84oRYI!c0vZBCl;q&j^Wkf}{ e+uYhFxOy23Vecw%=fq6_;Z3X&;HZgK zY1LfSvQ(F;Hgl%UT50E6Rl`~r2CLAOW?%M7?g1<_MXExofEv2@z5Tuk=I$PiN@D0s zTfCd y!%fImrCanX!RW^jE3Df(1~OM1xT6oZVBbYRj>#wnO{ zo|+`GnVs#`F*RnXWG6Z8b!I=lCcmBJoZChJkMC7wns_p2^7XI{r#*n@IYX~B!#ogR zOlT6gAq5M*#~BrBdd$~P&FmZsKbSZ$9_t 8WL_@A>Qcm7P$w6x)?9-(MdAPLd(0*S zkhr0RX15y8;h<;k5lrB8dc^NR2846F>eFVcY9@g1?Jm-l7o+-I%+nqdHoCs0&}=s> z?DXGMD8-uGUnTkbO@FbvT41f|(#}Dn%xFV@>_!_`*p-PNbJ^_Xbw3qD_K;Re=fS)R z_e4U~4iu!8cSHqGU%!EHfL|Ah)B%6n&xq7MGiakN!FG0??PMfDzD^s^sOFsEtIMRE zV4H;eA_%N{(s|;J;^}xkIn1gRm0tQ`$=y&bOnhe^l(^;DZ7OeO tq@yoX#4$;G^O)LQ=g=q(@lq)b>A*=H@mxy1J=1&$=^A?lTO_)l#39YQ>8=k^ zm~&c`E@4bOQGyNNKrF$Sh~dLLVPP!6y3BDP`#UzA>@I>0Kg* Lx_+7KT=$ om;f_*0EcZg?l*n zX>l~XdwUjs2d6Y6=?ALU)`6ast-`jVSY9kFg9XYb+lEo4ZL)Gd#>Qpc0$t~2!Mxsk z`973z41*Q_AUwwj;u1XfJ_T!B`yZ`m@4jH3vN$gU&sE|W&*UA@enDVCMIfO5ttcQw z&|P3YpnxpMnl}zXU;{F-NNCjwaP91JN3!W8P{|Fqi^PV}lvZB|k>XffE+?6=4wOt# zY`Gjx_q{|KPW76tHd6V(PHws@UWJFTyx$&u6~BKZ*yj9=WAYzBXuaq1j1{F~C0{Yg zj8?1Ja-~2y&5qaW@s! yPPg6dU^&Md0iW0NX@4opoq*35$~QV9DpFcPN^){+Vw{?Sin6l2 z;`R3Y`llrVF`z%-BU{$GM$u10*rtbz-d6PzU(k^$lxu`asFti2E0k*mi^!(5nxy{k z_m&Ga!ew+@UJqvr_I>$;gJLn*%yt9ClnZ8nOlJH3LefdKDy>Gl!BX0vo>_0a?kgZ3 zmCNRGz8WZ@Ub#IYOH7DzF(JZf9}_2xQgk|>?uPi2%j11}7M|z#dikgK%k%zfu ( N6Jwh{(y%8})eFDrzrt0CJ69iK=NHI;V{+r*cDa#0yxXyC{;s zFG9~p?Vdi! (Ed|s<}7A&NPp|sTKDv6 ulf{>4cEK3Nea!4X#6K&^4C>tYA W5>>j|6vzAEsWdBL!Irzul32428BP6n;xBh z-j5>ZCV&jv%pUen`nCs)o ih!Iea(R jX-G;F~W5+~{MJX+Mq8nHs{#5OWyQbLN!9dgwk7DS!-P&l$( zq@ZmKP;a=}sQjW?tVMRtAe_q)pRVBZN#jX%IA5@$KkkyBUc^C85(;0Rzm7!q*n _PNR$*tPzlZz;(il~CDJR%oms*gR}8Ky_i&nk8k@OHEOulB zF$!Zc2i>M%cUvJmYW2NHG4xn7^qe!u?FJisln=BiFwjvkz{6mQ`bo#pLW(8AtY+i6 z>Xf^LNaije4=*VZ!HY(oVW$XD7tJHSZc_oLiD!TtuK$+72{{d}JNpg54Y3Sn@I@>| z7?==DXM+s>{rzCWMV)xs@}nmZDsUx#C&Eq88WLS(Lbev4rj~YIW^lbEAK_?L|H4=K z{-HZNu@wPE4dqrnZAchZ;H&C_6wY)&+3v!7#}76D{dNyi^cqbnBIUD8y&jeR;F;bT zeSP*Q`@*{(dOtY#Hq7?^nEy7e1E=MBm^WZODTc!=VYDcbO|Lf?CY#FVhR<$ukT#z! z6sDgl1Q7$I*BPXkEr4*dSyHjZU>0Y&48(wSy1=xu$d#IB0pNqHpt5Y>(=NdA$ZVW2 zIiq#pVdzfbv|LV1hpZBwfQw?ls~@14(W{u`I_83}I2`r|XoCf#;k#p^;V~JF2ZB^b zWDzb_O{!KIjN%RFf8M-cqS<8P%HVO!;1$zkc3b1ITch;?tRAg8skQT{ZH8B7)wUAY z<<7Tyz1$^EXMUKhzK>_4n9*p|8;%B|tRxw-X2AaZp3z_^M3ZmPP;avOfB|#ckB!%H z>d7xlkv=VT66ONLL&d{pDuI+h>aTn+^}hNqE~j)|f62w=t4V#&)YE+M!8NOqLt$R;ed=V(&BdkE+%zUu*e2|WOh&KbEFp<3FTBOjQ zCpX;rFkblx;J@$8M-1M(cA}hQ+o Fdr2vvvvjOq^JUy|!C_^jNZ z71pFMm#kwXB&{YK?nzgO96d9 znhQcPoU>(ZsU(eentx@bDCGuT&~ncF&15hH;w#sAbmyXRO-5db`(!MXOwUn++L-sL zxa_ %NS~TC4T(y=t}1I*7Xv9 z7HY}b#P->8Q3sw@DLwUXot%8iEJC+bHB)e$ueT{=RBxgsh!Ob1p-)8jX68vxZHk!y zLf041kwvK$7B2k5Ns!v$)wQ!QDg3RnX4M;vnoaR{tG^(mxG9fQfk!E^VlCI8uPRy( zF%A9%*_@DrSPa}Ei0wqDv_9Fh3rUIPxnYRmi&JmWFXZJPg+7+Lz4Pw009IOU<6aLU zA3%EYo{PW?5@n&-P(|^|=TX-iO$jpn9zj-{qvKo*e@zpr7kCTY*8#X!lI8gKzAQuw zn73cW^i7z18lQjuDA0ra;*qr0Wn$73v?y;sMh? S~tTH&U11gX|SPE6!~{hmrgr)BMD-fX)gy|Gn%k>5a_ z*t3=Y^$SP=^}vFLKp=bc{6EoT%sv6Hd Zr~*B`b7BKmo`@CKr-2MUDwnSk{mSmw7*<{BVX1;{23V3J@E)J+B; zfrGG>;+&tTR(09`qC~bEPfx(Vf&9gQ>iRjzUqEo+zfcg0!7~Kp6kt_;u?jNJLOnnX z_JKzjDr!J22Td86a{$$Zdw;!PX`&L82zx4Gslc&{>dpeO;BO6Ms*f}~!fc`;3?1Cq zd}Is}b4n;G1+$RmNboad%8*Nsfj8vvkX%#bLs@8LCZ(1wSsJhB#uaUxh ^Z89M*$YGX3rW5heNEJ#Q4xS9Jru^T zhao>?eJc!&rAn53YC@-}lbQr~2+65Rmw0|i=c(+cqM?Z ZmHJsvN6I&ngqE zTDHjgsL{O=>f))Z%f5`~qR%TMza0G_)-6x4g7F~xDbc&E56jeZYV($5XjYYBiJpFB z*0^RbmnEH`l^~ixo`Asj5KFKif7W`_`66zsv@zh;I(T8yIabs9eqrf7+0#U?3%jxa z=ZdnW^HYx06(X2M@Y6u7j%5`y8_o_~KKKtIv?wO43~DKibExZJ>Yjb-F7Sli@1G*d zw&dR9R4*}#|M4)`2!4W*{|Q2Bd#9gHP93H?X0>T=I$tqAN3*~7e{lI>_{a1P?SK%@ zA~u2X_5(5C#{637LvtW4bpm{(y9*H(v@+;m(gV=HqAZ61L};#aC}oilL-Gtz03ak9 z80!J>I=Bnq@IFQdaGhW5eU~?|A3)#vixeox3U-U2t^&TZkSxGcg4(mdF1Wg8_66o` zh;-rBduDAYSCQfS^&Vt;0V})LBv|7jkaH4liGPxbmL!Ph<7CKS#;~90JSBVP50lHF zn=S0LvegRUES%Tl+)6-BA-Mvl6A~po*RC!gEeo4;)~S8t`Nkp-V;X4Xlh`NdQ$(b^ zNVNx$p}46&lff=jkBTzInwONU^j&k_h~k-NQ?>{IeMBv44sJJM5>QKU)lk-ZQG0ZI zb9=TI%{O@xxgn&)3q;Yx(M1_Wu7x>;pM^< 8&)oWL8a!)x4%M7tvV&cZRj>7 $DdG6P2@M$3P z(#9RnWAOd6ntyJt5FIF6X}MQR_wa9Bd7}jT{14xs sGw* z>)y%#3i3ym=ixe&HP2QaRy2PdC4_y>UP|=wmL)Q^&cZU$GoSLVW^otPR;K5XI&$9@ z-#Xsj!x%^EZs+qd8?vY}&eGX3r!%56HZsLCb~H3xWu?U@K_|H;v8=VMEve0OfJuXy zghLCQ;_-v>85TjX3-LiNLzD+g3}K%Jn)i+!$lEZwe$q8mRI?H==MgdjY((RJtIr-< zm^J;@f|t!-n040xr(st^u8bp0$H57s?Q=T_y*>7z_krbu&=0;Ik>6{*6&Il*B36tF zfTZt7k&W;>Qyfw;0 Tg|Ezw*AGCo|77xX z-nUzOM|o>`ZhL3FV&;i|j_oY+Qz(!z5Z+`yHrTF#U4XkGct>>)_CT8 j5!vsX-_r{>3oi&E3=R+a4onVk4~!0^5rYw{5=~1~ORS8&j7^MvQJ`NU z<00puOky^U5Y?B~8`gu}syOQU)bFC7LD7aH4VV}fIp}$i9%Crhx3tOdQ1K;9NDG{i z#46DzJ&j`>?mL-gq<%W-wrBC^=@Am7 o^u zYgKPb1%x1`o4|6^yYu{HnK`XzJ8%2$+;k9Bi#<;-9Cy8U(Pu4e`X5|N_P}EX$1)lq zYX15OC23VJo^2~5uLhH@xqn= z`Gl5u4>bIoY zL zfH=cnChW D9kcg5I)bL=|ZU@c`bn4eq}p!DCrZ5y|e|2YXmOiT#ck7Ii^Xm qu;JJI6baux0aV7kP#z8%m3JV z{6#mQfD{F_WYw;tCf~T$RcZ-K{U9SJ=XG<(bd;N!>6Dt9#z{)Y09&CdL78@N6|QY6 zl~^2(kVJ)%n~@<&ma-}a2NSgGh8YIK_c}lFG#HN1x@4drJCJ6=h)FZRz%!~v8!>Oq z%KAh6$^D>0#makW-V{7MEZX~xo75Z1&=HIXy@AV+Iw-a$ P#E+V^IxwOu>WA z&N->3 J?mU=3 zPv(kPphJ%>;;7R$(C0I!0vS|>>eGorms0mg0Zgq=zwRT@?E0j$OwohG7ph(FYnQ7j zX~X`qrhS=JdTnc6t!i=ESG(BozUw~leopvqltk)E#>Yk0Hl$q(oIgW72Mt@Jl-b3- zS6O(k(Q)CaRcKMAxJ;jQKJ`D$7sY0(IvS|Clq`6mYLJ|vrib92!^IGkUGCNKe!kQr z7s;R;e7`rMr6k$;$=0%AP7fHwa8j4m_`mx1e$JTyo$Lr|Zt2l)YinsqRmNBjVPy&~ zbpYf=r#^j|xmcID7Vtv~h)AF_)pYf0*ml4~TL1tLMK+vhUoxwpzOA-?)*V(0O&u0R zd3myXO>1}l5TqXQCwwDNitITG)RD06uojT24o!wO0U9#xsNn)b{{S+hfFlLnKhnR3 zhYbFJpsUCQVXlTSK0llO9{^-Po4+bH97qfqgpjKy<(9n9HqI!|I8g0)K&-r6SkQGr zQ1g{Wl>?!`unDP}+TDbiHuA_Z2xRXqq*9_NQ-`_Ao3f$aRW@{Q(Mb#6E;Y`1kpl|o z-s2rDe-L4)2n{nL2xyU^OR01;WTh+Vjg5_Th334G2u&Xx9Gui>T2*PlU8RI<)_8z6 zaWCL*st2VP0e4$;D73d%t~KN)yDP(lLa@<50%yIykfWp lJOtaZ6tI$F$CM2BM(b1caS63xzb@lPh(a|h4J0!`W(8c}zVgkLAB~FBR3(=A^ zRQ3bPxX;yOg+Ay#=(Q}n@)LA}t10w@f2sbmyUy+`nR*57Koi)9Gic@^Vs|wmB53UN zB3hhAU9FGzw=lZ*cz@eNf)>&Zb+9l7;i(~jxM*GwR#yuR*TlpGFifMN$UH?E$3PM} zmyBI(!li2^?Sq*xeYCK!AV2{Iv~vETp>bf9UWbew)SF!5BQu}2W8{2IC$C#V2t!54 z2K4Z?(u#J+Xwm}uZ5dT$9Ay$VpoE3sH-x)VlL}B&MnxIlTWI4M7a6(H2@h7%qF->C zvqd$C6PB0Dng();%07IU;ItbzP6R=NpLlw@ZS(>e!{2H2ENPj9(cggU1a4lygBNzL z{}=z>Y<&4;=IE%Q(8oVl`&!crwIBU4hX2;L%)UMzh&*7f|LQs-=cnb|0PILVQ^k)6 z-wb8^3jW476ui4jJ`>IupeWmCQ2T^!l6*z^)cle8hm=pzXXrEd{)fyTosZ{*@q7p& zt8kZ``X^0sjsBB@{y@U2N#vBXO*#Du`k!EQf2R!_LW|-%+q>sf+M+q!db;aV1U?4v zs{r>&j^Nd+S5;L-4(V4`#)EaUmAQBCs5IAFqtCUy1>!9j4ElqvUs*5jcDqH+?Z(vH z<&}Q}VW Tm1bF&P?63xQsb;L5VbAF?Q#35p7icL#X zi5R47)j*Vm3`C*)Dy(ibk6fdmUq)Rp0?k~Ez|gXDdeDx}Ho*egJVW+DFoWJ-dc2Q+ z(t>MWQFefp0TrQGAhT(E7p~^sg{xT7F{Hi=UvuxqSG)AO(0U`gC5&-tcWv?i{Fndo zU;fYHTJrGlFuAr2mgw@@iD`cEMWgY>7p8ea)Lt1``8dN{QMn@9=66s(EVUnP&(9M> zC6(&w0X7_Av1yu!6`WEa5RjZgVQp=#APhn@V^Gj3>iYFo)nUL!1JQJxp(tcDWZM*M z8nj;t2~$(DWqH}}&txVh&gpMFiqRx$I&_#Os*1RC6c!~z(~P7976+4LWPx*p&_OwJ z>(;@6FH0d7FvcPZn0ga%wpkk;ttoL!IeVPhUR_<4d7*Ja5G4rb=Q@EfRNy0gN{x(+ zP^TE5W=~I{VuA3HdvkLWbpPPs;K|7eeDQj{pZiM8J`8@qlu9-$%xATg4u^&g6*ru9 z&`7~a6Dz ssmf zB@n`)W-vB?q}S`Rv5AiI&-OYJa)Fypa;(zwzY`thn6 B@6x0*9Oyp0`$^}i2JAoiqG9`O3)RO`txe<|3SQ$9c z{R0Dk`A36r2o|Fpi VE)6E +Omkw_udCG=n86@ z%b0;l7;NFBWZo6a)@Hdnnx98? ?AMLL5lhhx5R0%-;csZ`!-|a8*FU#tcPQhY;K?cSr|9pazyJAb&t|ac z*{tiRCxw{d?9*Ycwmu2Hl1Wk(eCG~$Hp3pjL1l955^q#^szOFdp;YT#!T Jb*u4Q+qFM~S1mKL$xUgB}Wz$gTo5Jh}sxeBw8 @O z^9}}H6bt!l*9trL?%mtL*REmcRXZz|t5uoah9dJ$DxU evBnT8$K1v^C3|vmGtgLV` z7%vP)UX-%BYz|Q a9$bk?f7I{X&z30BxueW_c$Ol8X1#2hK8So>>Gk^L zF#}UBsYhxZsYw&}i+i+ZpmAUIq@dD{zH1W&Xe&4z=coBG!suHFp=cJs5`?g}j?1MY z*p$Um*#!omvsOw&OIibh#IYF#-``V^IcHxuLO$5cfPmDEg#{%V9UU9bW`~DIqhW~$ z+l-gO$zS~97n^yiXLxwHhb}_*hM`z3PGXaBEQ4kHq{Nnp?5wgbh*`Jz9He4-rCr zlntb;jl%UpHa8q~;sQKbz>a~TY^Dm#$Z#C0)#C03ve+W95I@Sm861EQmgp 2x}5R^LD?yd0CPLI^%WHm>mE#fv Ai;-@$XR47hGA5)d)uq)>yotcVs(43ky>A0PZ_Sk4?p}c2E1>@49gK5I4ue& zAvlXc7h5Hoti*yd|E7l6y%Zt*9>9MD@S)RG>h#@fZAIhXvf!bGk3U{0VT;9rOWC8H zy}fXFYkTJ?%bo7+?VVae6W{*!x32~i2Td1?=p74ht?&;ZjQ#{dXv`z%%wWvN)EeL+ z4zhL#ui05sS97^sv1U4fG+pK?1V~OnWQ*qDP~94xM8GJh@?% D2vh!7cdJ*HJc!$Gb!I(8crmsB9Vej}gkPi4(7#}aK zTqo3TA=EEc>b%ca1;XD`tGdh)@xp<4iD-F{FZoJcXF&ywO?b=cWR