[
  {
    "path": ".eslintrc.js",
    "content": "/** @type {import('eslint').Linter.Config} */\nmodule.exports = {\n    root: true,\n    extends: [\n        './configs/base.eslintrc.json',\n        './configs/warnings.eslintrc.json',\n        './configs/errors.eslintrc.json',\n        './configs/xss.eslintrc.json'\n    ],\n    ignorePatterns: [\n        '**/{node_modules,lib}',\n        'plugins'\n    ],\n    parserOptions: {\n        tsconfigRootDir: __dirname,\n        project: ['./configs/tsconfig.eslint.json', './theia-extensions/*/tsconfig.json', 'applications/electron/tsconfig.eslint.json']\n    }\n};\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug Report\nabout: Create a report to help us improve\n---\n\n<!-- Please provide a detailed description of the bug. -->\n### Bug Description:\n\n<!-- Please provide clear steps to reproduce the bug. -->\n### Steps to Reproduce:\n\n1.\n2.\n3.\n\n<!-- Please provide any additional information available. -->\n<!-- Additional information can be in the form of logs, screenshots, screencasts. -->\n\n### Additional Information\n\n- Operating System:\n- Theia Version:\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Question\n    url:  https://github.com/eclipse-theia/theia/discussions\n    about: Please ask questions here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature Request\nabout: Propose an idea for the project\n---\n\n<!-- Please fill out the following content for a feature request. -->\n\n<!-- Please provide a clear description of the feature and any relevant information. -->\n### Feature Description:\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nThank you for your Pull Request. Please provide a description and review\nthe requirements below.\n\nContributors guide: https://github.com/theia-ide/theia/blob/master/CONTRIBUTING.md\n-->\n\n#### What it does\n<!-- Include relevant issues and describe how they are addressed. -->\n\n#### How to test\n<!-- Explain how a reviewer can reproduce a bug, test new functionality or verify performance improvements. -->\n\n#### Review checklist\n\n- [ ] as an author, I have thoroughly tested my changes and carefully followed [the review guidelines](https://github.com/theia-ide/theia/blob/master/doc/pull-requests.md#requesting-a-review)\n\n#### Reminder for reviewers\n\n- as a reviewer, I agree to behave in accordance with [the review guidelines](https://github.com/theia-ide/theia/blob/master/doc/pull-requests.md#reviewing)\n\n"
  },
  {
    "path": ".github/workflows/build-next-release.yml",
    "content": "name: Build Theia IDE Next Release (Linux)\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: \"0 2 * * 1-5\" # Runs every weekday at 2am UTC\n\njobs:\n  build:\n    name: Build Next Release (Linux only)\n    runs-on: ubuntu-22.04\n    timeout-minutes: 60\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 0\n\n      - name: Use Node.js 24\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 24.x\n          registry-url: \"https://registry.npmjs.org\"\n\n      - name: Use Python 3.13\n        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: \"3.13\"\n\n      - name: Export build variables\n        shell: bash\n        run: |\n          THEIA_VERSION=next\n          echo \"THEIA_VERSION=${THEIA_VERSION}\" >> $GITHUB_ENV\n          yarn version --minor --no-git-tag-version\n          IDE_VERSION=$(jq -r .version package.json)\n          echo \"THEIA_IDE_VERSION=${IDE_VERSION}-next-$(date +%Y-%m-%d)\" >> $GITHUB_ENV\n\n      - name: Build next package\n        shell: bash\n        run: |\n          yarn --skip-integrity-check --network-timeout 100000\n          yarn version --new-version $THEIA_IDE_VERSION --no-git-tag-version\n          yarn lerna version $THEIA_IDE_VERSION --no-push --no-git-tag-version --yes\n          yarn update:theia $THEIA_VERSION && yarn update:theia:children $THEIA_VERSION\n          yarn --skip-integrity-check --network-timeout 100000\n          yarn build:extensions\n          yarn build:applications:next\n          yarn download:plugins\n          yarn package:applications:next\n        env:\n          NODE_OPTIONS: --max_old_space_size=4096\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Test (Linux)\n        run: xvfb-run -a yarn --cwd applications/electron-next test\n\n      - name: Upload Linux Dist Files\n        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0\n        with:\n          name: theia-ide-next-linux\n          path: |\n            applications/electron-next/dist/TheiaIDENext.AppImage\n          retention-days: 7\n\n      - name: Publish to rolling next release\n        shell: bash\n        run: |\n          # Delete existing next release if present\n          gh release delete next --yes --cleanup-tag 2>/dev/null || true\n          # Create a new pre-release with the next tag\n          gh release create next \\\n            --title \"Next Build - Ubuntu AppImage (${{ env.THEIA_IDE_VERSION }})\" \\\n            --notes \"Automated next build of the Ubuntu AppImage from \\`master\\` ($(date -u '+%Y-%m-%d %H:%M UTC')).\" \\\n            --prerelease \\\n            --target ${{ github.sha }} \\\n            applications/electron-next/dist/TheiaIDENext.AppImage \\\n            applications/electron-next/dist/latest-linux.yml\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/build-next.yml",
    "content": "name: Build Theia IDE next version\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: \"0 3 * * 1\" # Runs every monday at 3am\njobs:\n  build:\n    name: Build ${{ matrix.os }} next\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ubuntu-22.04, macos-15, macos-15-intel, windows-2022] # macos-15-intel is for x64, macOS-15 is for arm64\n\n    runs-on: ${{ matrix.os }}\n    timeout-minutes: 60\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 0 # To fetch all history for all branches and tags. (Will be required for caching with lerna: https://github.com/markuplint/markuplint/pull/111)\n\n      - name: Use Node.js 24\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: 24.x\n          registry-url: \"https://registry.npmjs.org\"\n\n      - name: Use Python 3.11\n        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: \"3.13\"\n\n      - name: Export build variables\n        shell: bash\n        run: |\n          THEIA_VERSION=next\n          echo \"THEIA_VERSION=${THEIA_VERSION}\" >> $GITHUB_ENV\n          yarn version --minor --no-git-tag-version\n          echo \"THEIA_IDE_VERSION=$(jq -r .version package.json)-${THEIA_VERSION}-$(date +%d-%m-%y)\" >> $GITHUB_ENV\n\n      - name: Update electron-builder.yml for macos-15\n        if: matrix.os == 'macos-15'\n        run: |\n          sed -i '' 's|https://download.eclipse.org/theia/ide/latest/macos|https://download.eclipse.org/theia/ide/latest/macos-arm|g' applications/electron/electron-builder.yml\n\n      - name: Build prod package\n        shell: bash\n        run: |\n          yarn --skip-integrity-check --network-timeout 100000\n          yarn version --new-version $THEIA_IDE_VERSION --no-git-tag-version\n          yarn lerna version $THEIA_IDE_VERSION --no-push --no-git-tag-version --yes\n          yarn update:theia $THEIA_VERSION && yarn update:theia:children $THEIA_VERSION\n          yarn --skip-integrity-check --network-timeout 100000\n          yarn build\n          yarn download:plugins\n          yarn package:applications\n        env:\n          NODE_OPTIONS: --max_old_space_size=4096\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # https://github.com/microsoft/vscode-ripgrep/issues/9\n\n      - name: Rename AppImage with version\n        if: runner.os == 'Linux'\n        run: |\n          mv applications/electron/dist/TheiaIDE.AppImage applications/electron/dist/TheiaIDE-$THEIA_IDE_VERSION.AppImage\n\n      - name: Upload Linux Dist Files\n        if: runner.os == 'Linux'\n        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0\n        with:\n          name: theia-ide-next-linux\n          path: |\n            applications/electron/dist/TheiaIDE-${{ env.THEIA_IDE_VERSION }}.AppImage\n          retention-days: 7\n\n      - name: Upload Mac Dist Files\n        if: runner.os == 'macOS'\n        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0\n        with:\n          name: ${{ matrix.os == 'macos-15-intel' && 'theia-ide-next-mac-x64' || matrix.os == 'macos-15' && 'theia-ide-next-mac-arm64'}}\n          path: applications/electron/dist/*.dmg\n          retention-days: 7\n\n      - name: Upload Windows Dist Files\n        if: runner.os == 'Windows'\n        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0\n        with:\n          name: theia-ide-next-windows\n          path: applications/electron/dist/*.exe\n          retention-days: 7\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build, package and test\n\non:\n  push:\n    branches:\n      - master\n    paths-ignore:\n      - '**/*.md'\n      - 'TheiaIDE logo/**'\n  workflow_dispatch:\n  pull_request:\n    branches:\n      - master\n    paths-ignore:\n      - '**/*.md'\n      - 'TheiaIDE logo/**'\n  schedule:\n    - cron: \"0 4 * * *\" # Runs every day at 4am: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#scheduled-events-schedule\n\njobs:\n  build:\n    name: ${{ matrix.os }}, Node.js v${{ matrix.node }}\n\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [windows-2022, ubuntu-22.04, macos-15, macos-15-intel] # macos-15-intel is for x64, macOS-15 is for arm64\n        node: [\"24.x\"]\n\n    runs-on: ${{ matrix.os }}\n    timeout-minutes: 60\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 0 # To fetch all history for all branches and tags. (Will be required for caching with lerna: https://github.com/markuplint/markuplint/pull/111)\n\n      - name: Use Node.js ${{ matrix.node }}\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: ${{ matrix.node }}\n          registry-url: \"https://registry.npmjs.org\"\n\n      - name: Use Python 3.13\n        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: \"3.13\"\n\n      - name: Install dependencies\n        shell: bash\n        run: yarn --skip-integrity-check --network-timeout 100000\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Lint\n        shell: bash\n        run: yarn lint\n\n      - name: Build dev package (Windows, Linux)\n        if: (runner.os == 'Windows' || runner.os == 'Linux') && github.event_name == 'pull_request'\n        shell: bash\n        run: |\n          yarn build:dev\n          yarn download:plugins\n          yarn package:applications:preview\n        env:\n          NODE_OPTIONS: --max_old_space_size=4096\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # https://github.com/microsoft/vscode-ripgrep/issues/9\n\n      - name: Build prod package (Windows, Linux)\n        if: (runner.os == 'Windows' || runner.os == 'Linux') && github.event_name != 'pull_request'\n        shell: bash\n        run: |\n          yarn build\n          yarn download:plugins\n          yarn package:applications\n        env:\n          NODE_OPTIONS: --max_old_space_size=4096\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # https://github.com/microsoft/vscode-ripgrep/issues/9\n\n      - name: Update electron-builder.yml for macos-15\n        if: matrix.os == 'macos-15'\n        run: |\n          sed -i '' 's|https://download.eclipse.org/theia/ide/latest/macos|https://download.eclipse.org/theia/ide/latest/macos-arm|g' applications/electron/electron-builder.yml\n\n      - name: Build prod package (Mac)\n        if: runner.os == 'macOS'\n        shell: bash\n        run: |\n          yarn build\n          yarn download:plugins\n          yarn package:applications\n        env:\n          NODE_OPTIONS: --max_old_space_size=4096\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # https://github.com/microsoft/vscode-ripgrep/issues/9\n\n      - name: Upload Mac Dist Files\n        if: runner.os == 'macOS'\n        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0\n        with:\n          name: ${{ matrix.os == 'macos-15-intel' && 'mac-x64' || matrix.os == 'macos-15' && 'mac-arm64'}}\n          path: |\n            applications/electron/dist/**\n            !applications/electron/dist/mac/**\n            !applications/electron/dist/mac-arm64/**\n          retention-days: 1\n\n      - name: Upload Windows Dist Files\n        if: runner.os == 'Windows' && github.event_name != 'pull_request'\n        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0\n        with:\n          name: windows\n          path: |\n            applications/electron/dist/**\n          retention-days: 1\n\n      - name: Upload Linux Dist Files\n        if: runner.os == 'Linux' && github.event_name != 'pull_request'\n        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0\n        with:\n          name: linux\n          path: |\n            applications/electron/dist/**\n          retention-days: 1\n\n      - name: Test (Linux)\n        if: runner.os == 'Linux'\n        run: |\n          xvfb-run -a yarn electron test\n\n      - name: Test (Windows)\n        if: runner.os == 'Windows'\n        shell: bash\n        run: |\n          yarn electron test\n\n      - name: Test (macOS)\n        if: runner.os == 'macOS'\n        shell: bash\n        run: |\n          yarn electron test\n\n      - name: Upload test screenshots\n        if: always()\n        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0\n        with:\n          name: test-screenshots-${{ matrix.os }}\n          path: applications/electron/test-screenshots/\n          if-no-files-found: ignore\n          retention-days: 5\n"
  },
  {
    "path": ".github/workflows/license-check-workflow.yml",
    "content": "name: 3PP License Check\n\non:\n  push:\n    branches:\n      - master\n    paths:\n      - 'yarn.lock'\n  workflow_dispatch:\n  pull_request:\n    branches:\n      - master\n    paths:\n      - 'yarn.lock'\n  schedule:\n    - cron: '0 4 * * *' # Runs every day at 4am: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#scheduled-events-schedule\n\njobs:\n  License-check:\n    name: 3PP License Check using dash-licenses\n\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ubuntu-latest]\n        node: [24]\n        java: [17]\n\n    runs-on: ${{ matrix.os }}\n    timeout-minutes: 60\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 2\n\n      - name: Use Node.js ${{ matrix.node }}\n        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0\n        with:\n          node-version: ${{ matrix.node }}\n\n      - name: Use Java ${{ matrix.java }}\n        uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0\n        with:\n          distribution: 'adopt'\n          java-version: ${{ matrix.java }}\n      \n      - name: Run daily dash-licenses check (non-review mode)\n        if: ${{ github.event_name == 'schedule' }}\n        shell: bash\n        run: |\n          yarn --frozen-lockfile --ignore-scripts\n          # Workaround: pre-download dash-licenses JAR (see comments in step below)\n          DASH_JAR=\"node_modules/@eclipse-dash/nodejs-wrapper/download/dash-licenses.jar\"\n          mkdir -p \"$(dirname \"$DASH_JAR\")\"\n          SNAP_BASE=\"https://repo.eclipse.org/repository/dash-maven2-snapshots/org/eclipse/dash/org.eclipse.dash.licenses/1.1.1-SNAPSHOT\"\n          SNAP_JAR=$(curl -s \"$SNAP_BASE/maven-metadata.xml\" | grep -oP '(?<=<value>)1\\.1\\.1-[^<]+' | tail -1)\n          curl -L \"$SNAP_BASE/org.eclipse.dash.licenses-$SNAP_JAR.jar\" -o \"$DASH_JAR\"\n          yarn license:check\n\n      - name: Run dash-licenses check in review mode\n        if: ${{ github.event_name != 'schedule' }}\n        shell: bash\n        run: |\n          yarn --frozen-lockfile --ignore-scripts\n          # Workaround: pre-download dash-licenses JAR since the @eclipse-dash/nodejs-wrapper\n          # uses a broken Nexus 2 URL after the repo.eclipse.org upgrade to Nexus 3.\n          # We use 1.1.1-SNAPSHOT because 1.0.2 and 1.1.0 incorrectly flag internal\n          # workspace packages (link:true with no version) as restricted.\n          # See: https://github.com/eclipse-dash/nodejs-wrapper/issues/7\n          # See: https://github.com/eclipse-dash/dash-licenses/issues/534\n          # TODO: remove this workaround once the nodejs-wrapper is updated (GH-17168)\n          DASH_JAR=\"node_modules/@eclipse-dash/nodejs-wrapper/download/dash-licenses.jar\"\n          mkdir -p \"$(dirname \"$DASH_JAR\")\"\n          SNAP_BASE=\"https://repo.eclipse.org/repository/dash-maven2-snapshots/org/eclipse/dash/org.eclipse.dash.licenses/1.1.1-SNAPSHOT\"\n          SNAP_JAR=$(curl -s \"$SNAP_BASE/maven-metadata.xml\" | grep -oP '(?<=<value>)1\\.1\\.1-[^<]+' | tail -1)\n          curl -L \"$SNAP_BASE/org.eclipse.dash.licenses-$SNAP_JAR.jar\" -o \"$DASH_JAR\"\n          yarn license:check:review\n        env:\n          DASH_TOKEN: ${{ secrets.DASH_LICENSES_PAT }}"
  },
  {
    "path": ".github/workflows/publish-builder-img.yml",
    "content": "name: Publish builder image\n\non:\n  schedule:\n    - cron: \"0 0 1 * *\" # runs 1st day of every month\n  workflow_dispatch:\n\njobs:\n  build:\n    name: Build and push builder image to Docker Hub\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Login to Docker Hub\n        uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # 4.1.0\n        with:\n          username: ${{ secrets.DOCKERHUB_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_TOKEN }}\n      \n      - name: Build and push Docker image\n        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0\n        with:\n          context: .\n          push: true\n          tags: eclipsetheia/theia-blueprint:builder"
  },
  {
    "path": ".github/workflows/publish-theia-ide-img.yml",
    "content": "name: Publish Theia IDE Docker Image\n\non:\n  workflow_dispatch:\n    inputs:\n      tag:\n        description: The image's tag\n        required: true\n        default: next\n\njobs:\n  build:\n    name: Build and push Theia IDE image to Github Packages\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Set up QEMU\n        uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0\n        with:\n          install: true\n          driver: docker-container\n          driver-opts: |\n            image=moby/buildkit:latest\n            network=host\n\n      - name: Login to Docker Hub\n        uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # 4.1.0\n        with:\n          username: ${{ secrets.DOCKERHUB_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_TOKEN }}\n\n      - name: Log in to the Github Container registry\n        uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # 4.1.0\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: List docker buildx available platforms\n        shell: bash\n        run: |\n          docker buildx inspect --bootstrap\n      \n      - name: Build and push Docker image\n        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0\n        with:\n          context: .\n          file: browser.Dockerfile\n          push: true\n          tags: |\n            ghcr.io/${{ github.repository }}/theia-ide:${{ github.event.inputs.tag }}\n            ghcr.io/${{ github.repository }}/theia-ide:latest\n          platforms: linux/amd64,linux/arm64\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n**/node_modules\n**/.browser_modules\n**/dist\n**/lib\n**/src-gen\n**/gen-webpack.config.js\n**/gen-webpack.node.config.js\n**/plugins\n**/tsconfig.tsbuildinfo\n*.log\nlicense-check-summary.txt*\n.theia-ide/chatSessions\n.prompts\n**/test-screenshots"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  // Use IntelliSense to learn about possible Node.js debug attributes.\n  // Hover to view descriptions of existing attributes.\n  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"node\",\n      \"request\": \"attach\",\n      \"name\": \"Attach by Process ID\",\n      \"processId\": \"${command:PickProcess}\"\n    },\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"Launch with Node.js\",\n      \"program\": \"${file}\"\n    },\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"Launch Electron Backend\",\n      \"runtimeExecutable\": \"${workspaceFolder}/node_modules/.bin/electron\",\n      \"windows\": {\n        \"runtimeExecutable\": \"${workspaceFolder}/node_modules/.bin/electron.cmd\"\n      },\n      \"cwd\": \"${workspaceFolder}/applications/electron\",\n      \"protocol\": \"inspector\",\n      \"args\": [\n        \"scripts/theia-electron-main.js\",\n        \"--log-level=debug\",\n        \"--hostname=localhost\",\n        \"--no-cluster\",\n        \"--app-project-path=${workspaceFolder}/applications/electron\",\n        \"--remote-debugging-port=9222\",\n        \"--no-app-auto-install\",\n        \"--plugins=local-dir:../../plugins\"\n      ],\n      \"env\": {\n        \"NODE_ENV\": \"development\"\n      },\n      \"sourceMaps\": true,\n      \"outFiles\": [\n        \"${workspaceFolder}/applications/electron/src-gen/**/*.js\",\n        \"${workspaceFolder}/applications/electron/lib/**/*.js\",\n        \"${workspaceFolder}/theia-extensions/*/lib/**/*.js\",\n        \"${workspaceFolder}/node_modules/@theia/*/lib/**/*.js\"\n      ],\n      \"smartStep\": true,\n      \"internalConsoleOptions\": \"openOnSessionStart\",\n      \"outputCapture\": \"std\"\n    },\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"Launch Browser Backend\",\n      \"program\": \"${workspaceFolder}/applications/browser/lib/backend/main.js\",\n      \"cwd\": \"${workspaceFolder}/applications/browser\",\n      \"args\": [\n        \"--hostname=0.0.0.0\",\n        \"--port=3000\",\n        \"--no-cluster\",\n        \"--app-project-path=${workspaceFolder}/applications/browser\",\n        \"--plugins=local-dir:../../plugins\",\n        \"--log-level=debug\"\n      ],\n      \"env\": {\n        \"NODE_ENV\": \"development\"\n      },\n      \"sourceMaps\": true,\n      \"outFiles\": [\n        \"${workspaceFolder}/applications/browser/src-gen/**/*.js\",\n        \"${workspaceFolder}/applications/browser/lib/**/*.js\",\n        \"${workspaceFolder}/theia-extensions/**/lib/**/*.js\",\n        \"${workspaceFolder}/node_modules/@theia/*/lib/**/*.js\"\n      ],\n      \"smartStep\": true,\n      \"internalConsoleOptions\": \"openOnSessionStart\",\n      \"outputCapture\": \"std\"\n    },\n    {\n      \"type\": \"node\",\n      \"request\": \"attach\",\n      \"name\": \"Attach to Plugin Host\",\n      \"port\": 9339,\n      \"timeout\": 60000,\n      \"stopOnEntry\": false,\n      \"smartStep\": true,\n      \"sourceMaps\": true,\n      \"internalConsoleOptions\": \"openOnSessionStart\",\n      \"outFiles\": [\n        \"${workspaceFolder}/plugins/**/*.js\"\n      ]\n    },\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"Launch Browser Backend (eclipse.jdt.ls)\",\n      \"program\": \"${workspaceFolder}/applications/browser/lib/backend/main.js\",\n      \"cwd\": \"${workspaceFolder}/applications/browser\",\n      \"args\": [\n        \"--hostname=0.0.0.0\",\n        \"--port=3000\",\n        \"--no-cluster\",\n        \"--root-dir=${workspaceFolder}/../eclipse.jdt.ls/org.eclipse.jdt.ls.core\",\n        \"--app-project-path=${workspaceFolder}/applications/browser\",\n        \"--plugins=local-dir:../../plugins\",\n        \"--log-level=debug\",\n        \"--no-app-auto-install\"\n      ],\n      \"env\": {\n        \"NODE_ENV\": \"development\"\n      },\n      \"sourceMaps\": true,\n      \"outFiles\": [\n        \"${workspaceFolder}/applications/browser/src-gen/**/*.js\",\n        \"${workspaceFolder}/applications/browser/lib/**/*.js\",\n        \"${workspaceFolder}/theia-extensions/**/lib/**/*.js\",\n        \"${workspaceFolder}/node_modules/@theia/*/lib/**/*.js\"\n      ],\n      \"smartStep\": true,\n      \"internalConsoleOptions\": \"openOnSessionStart\",\n      \"outputCapture\": \"std\"\n    },\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"protocol\": \"inspector\",\n      \"name\": \"Run Mocha Tests\",\n      \"program\": \"${workspaceFolder}/node_modules/mocha/bin/_mocha\",\n      \"args\": [\n        \"--no-timeouts\",\n        \"--colors\",\n        \"--opts\",\n        \"${workspaceFolder}/configs/mocha.opts\",\n        \"**/${fileBasenameNoExtension}.js\"\n      ],\n      \"env\": {\n        \"TS_NODE_PROJECT\": \"${workspaceFolder}/tsconfig.json\"\n      },\n      \"sourceMaps\": true,\n      \"smartStep\": true,\n      \"internalConsoleOptions\": \"openOnSessionStart\",\n      \"outputCapture\": \"std\"\n    },\n    {\n      \"name\": \"Launch Browser Frontend\",\n      \"type\": \"chrome\",\n      \"request\": \"launch\",\n      \"url\": \"http://localhost:3000/\",\n      \"webRoot\": \"${workspaceFolder}/applications/browser\"\n    },\n    {\n      \"type\": \"chrome\",\n      \"request\": \"attach\",\n      \"name\": \"Attach to Electron Frontend\",\n      \"port\": 9222,\n      \"webRoot\": \"${workspaceFolder}/applications/electron\"\n    },\n    {\n      \"name\": \"Launch VS Code Tests\",\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"args\": [\n        \"${workspaceFolder}/applications/browser/lib/backend/main.js\",\n        \"${workspaceFolder}/plugins/vscode-api-tests/testWorkspace\",\n        \"--port\",\n        \"3030\",\n        \"--hostname\",\n        \"0.0.0.0\",\n        \"--extensionTestsPath=${workspaceFolder}/plugins/vscode-api-tests/out/singlefolder-tests\",\n        \"--hosted-plugin-inspect=9339\"\n      ],\n      \"env\": {\n        \"THEIA_DEFAULT_PLUGINS\": \"local-dir:${workspaceFolder}/plugins\"\n      },\n      \"stopOnEntry\": false,\n      \"sourceMaps\": true,\n      \"outFiles\": [\n        \"${workspaceFolder}/../.js\"\n      ]\n    }\n  ],\n  \"compounds\": [\n    {\n      \"name\": \"Launch Electron Backend & Frontend\",\n      \"configurations\": [\n        \"Launch Electron Backend\",\n        \"Attach to Plugin Host\",\n        \"Attach to Electron Frontend\"\n      ],\n      \"stopAll\": true\n    },\n    {\n      \"name\": \"Launch Browser Backend & Frontend\",\n      \"configurations\": [\n        \"Launch Browser Backend\",\n        \"Attach to Plugin Host\",\n        \"Launch Browser Frontend\"\n      ],\n      \"stopAll\": true\n    }\n  ]\n}"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"[markdown]\": {\n        \"editor.defaultFormatter\": \"davidanson.vscode-markdownlint\"\n    }\n}"
  },
  {
    "path": ".vscode/theia.code-snippets",
    "content": "{\n    \"Copyright-JS/JSX/TS/TSX/CSS\": {\n        \"prefix\": [\n            \"header\",\n            \"copyright\"\n        ],\n        \"body\": \"/********************************************************************************\\n * Copyright (C) $CURRENT_YEAR ${YourCompany} and others.\\n *\\n * This program and the accompanying materials are made available under the\\n * terms of the MIT License, which is available in the project root.\\n *\\n * SPDX-License-Identifier: MIT\\n ********************************************************************************/\\n\\n$0\",\n        \"description\": \"Adds the copyright...\",\n        \"scope\": \"javascript,javascriptreact,typescript,typescriptreact,css\"\n    }\n}\n"
  },
  {
    "path": "ADOPTER.md",
    "content": "# Adopter Guide\n\nThis repository serves as a template for building desktop products on the [Eclipse Theia platform](https://theia-ide.org). This guide documents packaging considerations when building your own Theia based applications.\n\n## Electron Packaging and Asar\n\nElectron applications may use [asar archives](https://github.com/electron/asar) to package application source files into a single read only archive. This improves startup performance and avoids path length issues on Windows.\n\nIn this repository, asar packaging is enabled via `asar: true` in `applications/electron/electron-builder.yml`. When the application is packaged with `electron-builder`, the contents of the app directory are bundled into an `app.asar` file inside the packaged application's `resources/` folder.\n\nCode that uses `__dirname` to access files, or executes scripts from the filesystem, will fail because asar archives are not real directories. At runtime, `__dirname` resolves to a path inside `app.asar`, but Node's `fs` module and the OS cannot access files inside the archive as regular filesystem entries.\n\nTwo categories of files are typically affected:\n\n1. **Native binaries**, `.node` files or prebuilt binaries\n2. **Scripts and resources** that must be accessible on the real filesystem\n\n### Mitigation Strategies\n\n#### 1. asarUnpack (electron-builder.yml)\n\nFiles matching `asarUnpack` glob patterns are automatically extracted alongside the asar archive during packaging. At runtime, these files live under `app.asar.unpacked/` instead of `app.asar/`.\n\nFor example:\n\n```yaml\nasarUnpack:\n  - \"**/lib/backend/native/**\"\n  - \"**/lib/backend/shell-integrations/**\"\n  - \"**/lib/build/Release/**\"\n  - \"**/lib/prebuilds/**\"\n```\n\n`asarUnpack` only extracts the files to the real filesystem. Code that resolves paths using `__dirname` will still get a path containing `app.asar`, so it must also handle the `.asar.unpacked` path segment for the files to be found.\n\n#### 2. patch-package\n\nWhen upstream code in `node_modules` needs modification, for example to adjust paths, [patch-package](https://github.com/ds300/patch-package) can apply source level patches after `yarn install`.\n\n* Patches live in the `patches` directory at the repository root\n* Patches are applied automatically via the `postinstall` script in `package.json`\n\n#### 3. Webpack Post Processing\n\nAnother option is to adjust the bundled code in Webpack.\n\nAs an example, the `PatchRipgrepPlugin` in `applications/electron/webpack.config.js` patches the bundled `main.js` after webpack emit to rewrite ripgrep's path resolution from `.asar` to `.asar.unpacked`:\n\n```js\nclass PatchRipgrepPlugin {\n    apply(compiler) {\n        compiler.hooks.afterEmit.tapAsync('PatchRipgrepPlugin', (compilation, callback) => {\n            // Reads main.js, finds the ripgrep path assignment, and rewrites\n            // .asar + path.sep → .asar.unpacked + path.sep\n            // ...\n        });\n    }\n}\n```\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "<div id=\"theia-logo\" align=\"left\">\n    <br />\n    <img src=\"https://raw.githubusercontent.com/eclipse-theia/theia/master/logo/EF_GRY-OR_svg.svg?sanitize=true\" alt=\"Eclipse Logo\" width=\"300\"/>\n</div>\n\n# Community Code of Conduct\n\nVersion 1.1\n\nOctober 21, 2019\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n- Using welcoming and inclusive language\n- Being respectful of differing viewpoints and experiences\n- Gracefully accepting constructive criticism\n- Focusing on what is best for the community\n- Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n- The use of sexualized language or imagery and unwelcome sexual attention or advances\n- Trolling, insulting/derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or electronic address, without explicit permission\n- Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at codeofconduct@eclipse.org. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\nFor answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq\n\n<div id=\"theia-logo\" align=\"left\">\n    <br />\n    <img src=\"https://www.eclipse.org/images/Eclipse_Code_of_Conduct.png\" alt=\"Eclipse Logo\" width=\"150\"/>\n</div>\n\n----\nNote: Please see [here](https://www.eclipse.org/org/documents/Community_Code_of_Conduct.php) for the latest version of this document, hosted at the Eclipse Foundation\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Eclipse Theia\n\nTheia is a young open-source project with a modular architecture. One of the\ngoals is to make sure that we can customize and enhance any Theia application\nthrough extensions.  So while the main Theia repository contains some common\nfunctionality for IDE-like applications, like a file system or a navigator\nview, most functionality doesn't necessarily need to be put into the core\nrepository but can be developed separately.\n\n## How Can I Contribute?\n\nIn the following some of the typical ways of contribution are described.\n\n### Asking Questions\n\nIt's totally fine to ask questions by opening an issue in the Theia GitHub\nrepository. We will close it once it's answered and tag it with the 'question'\nlabel. Please check if the question has been asked before there or on [Stack\nOverflow](https://stackoverflow.com).\n\n### Reporting Bugs\n\nIf you have found a bug, you should first check if it has already been filed\nand maybe even fixed. If you find an existing unresolved issue, please add your\ncase. If you could not find an existing bug report, please file a new one. In\nany case, please add all information you can share and that will help to\nreproduce and solve the problem.\n\n### Reporting Feature Requests\n\nYou may want to see a feature or have an idea. You can file a request and we\ncan discuss it.  If such a feature request already exists, please add a comment\nor some other form of feedback to indicate you are interested too. Also in this\ncase any concrete use case scenario is appreciated to understand the motivation\nbehind it.\n\n### Pull Requests\n\nBefore you get started investing significant time in something you want to get\nmerged and maintained as part of Theia, you should talk with the team through\nan issue. Simply choose the issue you would want to work on, and tell everyone\nthat you are willing to do so and how you would approach it. The team will be\nhappy to guide you and give feedback.\n\nWe follow the contributing and reviewing pull request guidelines described\n[here](https://github.com/eclipse-theia/theia/blob/master/doc/pull-requests.md).\n\n## Coding Guidelines\n\nWe follow the coding guidelines described\n[here](https://github.com/eclipse-theia/theia/wiki/Coding-Guidelines).\n\n## Eclipse Contributor Agreement\n\nBefore your contribution can be accepted by the project team contributors must\nelectronically sign the Eclipse Contributor Agreement (ECA).\n\n* https://www.eclipse.org/legal/ECA.php\n\nCommits that are provided by non-committers must have a Signed-off-by field in\nthe footer indicating that the author is aware of the terms by which the\ncontribution has been provided to the project. The non-committer must\nadditionally have an Eclipse Foundation account and must have a signed Eclipse\nContributor Agreement (ECA) on file.\n\nFor more information, please see the Eclipse Committer Handbook:\nhttps://www.eclipse.org/projects/handbook/#resources-commit\n\n## Sign your work\n\nThe sign-off is a simple line at the end of the explanation for the patch. Your\nsignature certifies that you wrote the patch or otherwise have the right to\npass it on as an open-source patch. The rules are pretty simple: if you can\ncertify the below (from\n[developercertificate.org](https://developercertificate.org/)):\n\n```\nDeveloper Certificate of Origin\nVersion 1.1\n\nCopyright (C) 2004, 2006 The Linux Foundation and its contributors.\n1 Letterman Drive\nSuite D4700\nSan Francisco, CA, 94129\n\nEveryone is permitted to copy and distribute verbatim copies of this\nlicense document, but changing it is not allowed.\n\nDeveloper's Certificate of Origin 1.1\n\nBy making a contribution to this project, I certify that:\n\n(a) The contribution was created in whole or in part by me and I\n    have the right to submit it under the open source license\n    indicated in the file; or\n\n(b) The contribution is based upon previous work that, to the best\n    of my knowledge, is covered under an appropriate open source\n    license and I have the right under that license to submit that\n    work with modifications, whether created in whole or in part\n    by me, under the same open source license (unless I am\n    permitted to submit under a different license), as indicated\n    in the file; or\n\n(c) The contribution was provided directly to me by some other\n    person who certified (a), (b) or (c) and I have not modified\n    it.\n\n(d) I understand and agree that this project and the contribution\n    are public and that a record of the contribution (including all\n    personal information I submit with it, including my sign-off) is\n    maintained indefinitely and may be redistributed consistent with\n    this project or the open source license(s) involved.\n```\n\nThen you just add a line to every git commit message:\n\n    Signed-off-by: Joe Smith <joe.smith@email.com>\n\nUse your real name (sorry, no pseudonyms or anonymous contributions.)\n\nIf you set your `user.name` and `user.email` git configs, you can sign your\ncommit automatically with `git commit -s`.\n"
  },
  {
    "path": "Dockerfile",
    "content": "# See the associated GitHub workflow, that builds and publishes\n# this docker image to Docker Hub:\n# .github/workflows/publish-builder-img.yml\n# It can be triggered manually from the GitHub project page. \n\n# We want to support as many Debian versions as possible.\n# Therefore, use the oldest Debian release that still provides the desired Node.js version.\nFROM node:24-bookworm\nRUN dpkg --add-architecture i386 \\\n && apt-get update \\\n && apt-get install -y --no-install-recommends \\\n      libxkbfile-dev libsecret-1-dev python3 \\\n      wine wine32 wine64 \\\n && rm -rf /var/lib/apt/lists/*\n\n# UID-agnostic wine tuning. WINEPREFIX is intentionally NOT set here; callers\n# must provide a writable path owned by the runtime user.\nENV WINEDLLOVERRIDES=\"mscoree=;mshtml=\" \\\n    WINEDEBUG=-all\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Eclipse Theia IDE Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "NOTICE.md",
    "content": "# Notices for Eclipse Theia\n\nThis content is produced and maintained by the Eclipse Theia project.\n\n* Project home: https://projects.eclipse.org/projects/ecd.theia\n\n## Trademarks\n\nEclipse Theia is a trademark of the Eclipse Foundation.\n\n## Copyright\n\nAll content is the property of the respective authors or their employers. For\nmore information regarding authorship of content, please consult the listed\nsource code repository logs.\n\n## Declared Project Licenses\n\nThis program and the accompanying materials are made available under the terms\nof the MIT License which is available at https://opensource.org/license/mit/.\n\nSPDX-License-Identifier: MIT\n\n## Source Code\n\nThe project maintains the following source code repositories:\n\n* https://github.com/eclipse/theia-generator-plugin\n* https://github.com/eclipse/theia-yeoman-plugin\n* https://github.com/eclipse/theia-plugin-packager\n* https://github.com/eclipse/theia-cpp-extension\n* https://github.com/eclipse/theia-python-extension\n* https://github.com/eclipse/theia-java-extension\n\n## Third-party Content\n\nThis project leverages the following third party content.\n\nchalk (2.4.1)\n\n* License: MIT\n* Project: https://github.com/chalk/chalk\n* Source: https://github.com/chalk/chalk\n\ncode copied from project cortex-debug (0.1.21)\n\n* License: MIT\n\nCode copied from project Microsoft/vscode (1.31.0)\n\n* License: MIT\n\nCode copied from project Microsoft/vscode (1.32.3)\n\n* License: MIT\n* Project: https://code.visualstudio.com/\n* Source: https://github.com/Microsoft/vscode\n\nCode copied from project Microsoft/vscode (1.33.1)\n\n* License: MIT\n\ncode copied from project microsoft/vscode (1.33.1)\n\n* License: MIT\n\nCode copied from VSCode (n/a)\n\n* License: MIT\n\nCopied code from project VSCode (n/a)\n\n* License: MIT\n\nCSS copied from VS Code (n/a)\n\n* License: MIT\n\ndugite (1.52.0)\n\n* License: MIT\n\nElectron (3.1.7)\n\n* License: BSD-2-Clause AND BSD-3-Clause AND (MIT OR GPL-2.0) AND Apache-2.0\n   AND (BSD-2-Clause OR MIT OR Apache-2.0) AND ISC AND MIT AND X11 AND\n   BSD-2-Clause-FreeBSD AND Public-Domain AND Unlicense AND MPL-2.0 AND\n   (BSD-3-Clause OR MPL-2.0) AND CC-BY-3.0 AND (AFL-2.0\n* Project: https://electronjs.org/\n* Source: https://github.com/electron/electron\n\nelectron@2.0.14 (2.0.14)\n\n* License: MIT AND BSD-2-Clause AND Apache-2.0 AND (AFL-2.1 OR BSD-3-Clause)\n   AND BSD-3-Clause AND ISC AND X11 AND Public-Domain AND (GPL-2.0 OR MIT) AND\n   Unlicense AND IJG AND ICU AND UNICODE-TOU AND NTP AND (MIT OR BSD-3-Clause)\n   AND Libpng AND MPL-2.0 AND LGPL-2.1+\n* Project: https://github.com/electron/electron\n* Source: https://github.com/electron/electron/releases/tag/v2.0.14\n\ngetmac (1.4.6)\n\n* License: MIT\n* Project: https://github.com/bevry/getmac\n* Source: https://github.com/bevry/getmac\n\nGH-3397: Implemented the HTTP-based authentication for Git in Electron. (n/a)\n\n* License: MIT\n\nglob promise (3.4.0)\n\n* License: ISC\n* Project: https://github.com/ahmadnassri/glob-promise\n* Source: https://github.com/ahmadnassri/glob-promise\n\nIcon configure-inverse.svg (n/a)\n\n* License: MIT\n* Project: https://github.com/Microsoft/vscode\n* Source:\n   https://github.com/Microsoft/vscode/blob/master/src/vs/workbench/contrib/tasks/common/media/configure-inverse.svg#L1\n\nlibffmpeg (FFmpeg) Delivered with Electron (3.1.7)\n\n* License: LGPL-2.1+\n\nlong.js (3.2.0)\n\n* License: Apache-2.0\n\nmicromatch (3.1.10)\n\n* License: MIT\n* Project: https://github.com/micromatch/micromatch\n* Source: https://github.com/micromatch/micromatch\n\nmonaco-typescript (2.3.0)\n\n* License: MIT\n* Project: https://github.com/Microsoft/monaco-typescript\n* Source: https://github.com/Microsoft/monaco-typescript.git\n\nnative-keymap (1.2.5)\n\n* License: Pending\n* Project: https://github.com/Microsoft/node-native-keymap\n* Source: https://github.com/Microsoft/node-native-keymap\n\nnode-oniguruma (n/a)\n\n* License: BSD-2-Clause AND GPL-2.0 WITH Autoconf-exception-2.0 AND\n   GPL-2.0-or-later WITH libtool-exception AND X11 AND MIT AND Public-Domain\n\nnode.js dependencies for Theia (n/a)\n\n* License: MIT AND BSD-3-Clause AND ISC AND Apache-2.0 AND BSD-2-Clause AND\n   Zlib AND X11 AND (BSD-3-Clause OR AFL-2.1) AND CC-By-4.0 AND CC-by-2.5-SA AND\n   CC0-1.0 AND (BSD-3-Clause OR MPL-2.0) AND Unlicense AND (MIT OR GPL-3.0) AND\n   (MIT OR GPL-2.0) AND (Apache-2.0 OR\n\nps-list (5.0.1)\n\n* License: MIT\n* Project: https://github.com/sindresorhus/ps-list\n* Source: https://github.com/sindresorhus/ps-list\n\nread-pkg (4.0.1)\n\n* License: MIT\n* Project: https://github.com/sindresorhus/read-pkg\n* Source: https://github.com/sindresorhus/read-pkg\n\nregular expressions and helper function copied from microsoft/vscode (1.33.1)\n\n* License: MIT\n\nrequestretry (3.1.0)\n\n* License: MIT\n* Project: https://github.com/FGRibreau/node-request-retry\n* Source: https://github.com/FGRibreau/node-request-retry\n\nrimraf (2.6.2)\n\n* License: ISC\n\ntextmate/tcl.tmbundle (n/a)\n\n* License: LicenseRef-Php_Tmbundle\n\ntheia npm node (n/a)\n\n* License: BSD-2-Clause OR (MIT OR Apache-2.0) AND (AFL-2.1 OR BSD-3-Clause)\n   AND Apache-2.0 AND Artistic-2.0 AND BSD-3-Clause AND (BSD-3-Clause OR MIT)\n   AND MPL-2.0 AND CC0-1.0 AND CC-BY-3.0 AND CC-BY-4.0 AND CC-BY-SA-2.5 AND\n   GPL-2.0 WITH Autoconf-ex\n\ntheia-cpp-extension npm node (n/a)\n\n* License: BSD-2-Clause OR (MIT OR Apache-2.0) AND MIT AND BSD-3-Clause AND\n   Zlib AND (MIT OR GPL-3.0) AND OFL-1.1 AND Apache-2.0 AND CC0-1.0 AND\n   CC-BY-3.0 AND ISC AND MPL-2.0 AND License-Ref-Public-Domain AND BSL-1.0 AND\n   (AFL-2.1 OR BSD-3.0) AND Unlicense AND Artist\n\ntslint (5.10.0)\n\n* License: Apache-2.0 AND MIT\n* Project: http://palantir.github.io/tslint/\n* Source: https://github.com/palantir/tslint\n\ntypefox/monaco-language-client (0.5.0)\n\n* License: MIT\n\ntypescript-formatter (7.2.2)\n\n* License: MIT\n* Project: https://github.com/vvakame/typescript-formatter\n* Source: https://github.com/vvakame/typescript-formatter\n\nVS Code (1.33.0)\n\n* License: MIT\n\nvscode (1.26.0)\n\n* License: MIT AND LicenseRef-Php_Tmbundle\n\nvscode (1.26.0)\n\n* License: MIT\n\nvscode (1.31.0)\n\n* License: MIT\n\nvscode-debugadapter-node (n/a)\n\n* License: MIT\n\nvscode-java (0.36.0)\n\n* License: EPL-1.0\n\nvscode-java (0.44.0)\n\n* License: EPL-1.0\n\nvscode-java-debug (0.15.0)\n\n* License: MIT\n\nwhen (3.7.8)\n\n* License: MIT\n* Project: https://github.com/cujojs/when\n* Source: https://github.com/cujojs/when\n\nwjordan/browser-path SHA6719d19077b1454bff8b802f9be79cb1b69ebe7e (n/a)\n\n* License: MIT\n\nxterm.js (3.9.1)\n\n* License: MIT\n* Project: https://xtermjs.org/\n* Source: https://github.com/xtermjs/xterm.js\n\nyargs (12.0.1)\n\n* License: MIT\n* Project: http://yargs.js.org/\n* Source: https://github.com/yargs/yargs\n\nyeoman environment (2.3.0)\n\n* License: BSD-2-Clause AND BSD-3-Clause\n* Project: https://github.com/yeoman/environment\n* Source: https://github.com/yeoman/environment\n\nyeoman generator (3.0.0)\n\n* License: BSD-2-Clause AND BSD-3-Clause\n* Project: http://yeoman.io\n* Source: https://github.com/yeoman/generator\n\nyeoman-generator (2.0)\n\n* License: BSD-2-Clause\n* Project: http://yeoman.io/\n* Source: https://github.com/yeoman/generator\n\nyosay (2.0.2)\n\n* License: BSD-2-Clause\n* Project: https://github.com/yeoman/yosay\n* Source: https://github.com/yeoman/yosay\n\n## Cryptography\n\nContent may contain encryption software. The country in which you are currently\nmay have restrictions on the import, possession, and use, and/or re-export to\nanother country, of encryption software. BEFORE using any encryption software,\nplease check the country's laws, regulations and policies concerning the import,\npossession, or use, and re-export of encryption software, to see if this is\npermitted.\n\n\n## Electron\n\nNOTICE:\n\nPlease note Electron combines Chromium and Node.js into a single runtime.\nWhile Electron, Chromium and Node.js are generally licensed under very\npermissive MIT and BSD-3-Clause licenses, both Electron and Chromium distribute\nFFmpeg.  While FFmpeg is under the LGPL-2.1-or-later license it incorporates\nseveral optional parts and optimizations that are covered by the\nGPL-2.0-or-later.  We understand both Electron and Chromium do not distribute\nversions of FFmpeg with GPL content enabled; however, FFmpeg may be configured\nenabled to work with proprietary codecs.  It is our understanding these\nproprietary codecs may be patented; and as a result, may be subject to\nlicensing fees.\n\nWe strongly recommend downstream consumers verify the type of FFmpeg support\nconfigured and modify as required.  More information on instructions to verify\ncan be found here\nhttps://electronjs.org/docs/development/upgrading-chromium#verify-ffmpeg-support\n\n\n"
  },
  {
    "path": "PUBLISHING.md",
    "content": "# Publishing Guide for the Eclipse Theia IDE\n\nThis document provides a unified, structured guide for publishing a new version of the Theia IDE. It covers everything from updating package versions, preview testing, releasing, promoting to stable, and other post-release activities.\n\n## Table of Contents\n\n1. [Overview](#1-overview)\n2. [Update Package Versions and Theia](#2-update-package-versions-and-theia)\n   - [2.1 Install build dependencies](#21-install-build-dependencies)\n   - [2.2 Update versions](#22-update-versions)\n   - [2.3 Check for mandatory code changes](#23-check-for-mandatory-code-changes)\n   - [2.4 Prepare Release PR](#24-prepare-release-pr)\n   - [2.5 Mac Artifacts](#25-mac-artifacts)\n   - [2.6 Merge Release PR & Trigger Jenkins Build](#26-merge-release-pr--trigger-jenkins-build)\n3. [Preview, Testing, and Release Process](#3-preview-testing-and-release-process)\n   - [3.1 Confirm the new preview version is published](#31-confirm-the-new-preview-version-is-published-do-not-promote-as-stable-yet)\n   - [3.2 Announce Preview Test Phase](#32-announce-preview-test-phase)\n   - [3.3 Patch Releases](#33-patch-releases)\n4. [Promote IDE from Preview to Stable Channel](#4-promote-ide-from-preview-to-stable-channel)\n5. [Tag the Release Commit](#5-tag-the-release-commit)\n6. [Publish Docker Image](#6-publish-docker-image)\n7. [Snap Update](#7-snap-update)\n8. [Upgrade Dependencies](#8-upgrade-dependencies)\n\n## 1. Overview\n<!-- release: both -->\n\nEvery commit to the master branch is automatically published as a preview version. Official updates require a version change.\n\nThis guide differentiates between two version numbers:\n\n- **THEIA_VERSION**: (used variable: {{version}}) The version of Theia used in this release.\n- **THEIA_IDE_VERSION**: (used variable: {{ideVersion}}) The Theia IDE release version. Depending on the context:\n  - If there was **no** Theia release, increment the patch version by 1 (e.g., 1.47.0 -> 1.47.1 or 1.47.100 -> 1.47.101).\n  - For a new Theia *minor* release (e.g., 1.48.0), use the same version as Theia.\n  - For a new Theia *patch* release (e.g., 1.48.1), use Theia's patch version multiplied by 100 (e.g., 1.48.100).\n\n## 2. Update Package Versions and Theia\n<!-- release: both -->\n\nFollow these steps to update dependencies and package versions:\n\n### 2.1. Install build dependencies\n<!-- release: both -->\n\n```sh\nyarn\n```\n\n### 2.2. Update versions\n<!-- release: both -->\n\n1. Update the monorepo version to **THEIA_IDE_VERSION** (without creating a Git tag):\n\n   ```sh\n   yarn version --no-git-tag-version --new-version {{ideVersion}}\n   ```\n\n2. Optional: If there is a new Theia release to consume, update Theia dependencies to **THEIA_VERSION** :\n\n   ```sh\n   yarn update:theia {{version}} && yarn update:theia:children {{version}}\n   ```\n\n3. Update all package versions to **THEIA_IDE_VERSION** (select in input prompt):\n\n   ```sh\n   yarn lerna version --exact --no-push --no-git-tag-version\n   ```\n\n4. Update the yarn lock file:\n\n   ```sh\n   yarn\n   ```\n\n### 2.3. Check for mandatory code changes\n<!-- release: both -->\n\nUpdate the code to include everything that should be part of the release:\n\n- Implement all tickets that are located in: <https://github.com/eclipse-theia/theia-ide/labels/toDoWithRelease>\n- If there was a Theia release:\n  - Review breaking changes\n  - Check for new built-ins\n  - Check sample applications changes\n  - Update code as necessary\n  - Check for new theia packages to be consumed in the example applications (in case there are no tickets)\n\n### 2.4. Prepare Release PR\n<!-- release: both -->\n\nAfter completing step 2.3, open a PR with your changes <https://github.com/eclipse-theia/theia-ide/compare>\n\n   PR Title:\n\n   ```md\n   Update to Theia v{{version}}\n   ```\n\n   OR (if it is a pure Theia IDE version update):\n\n   ```md\n   Publish Theia IDE {{ideVersion}}\n   ```\n\n### 2.5. Mac Artifacts\n<!-- release: both -->\n\n- The PR will trigger a verification build that generates two zip files with mac artifacts.\n- Download these two zips and replace them in this pre-release: <https://github.com/eclipse-theia/theia-ide/releases/tag/pre-release>.\n- These unsigned dmgs will be used as input for the Jenkins build.\n\n### 2.6. Merge Release PR & Trigger Jenkins Build\n<!-- release: both -->\n\n1. Merge PR\n\n2. ==> Steps 2.4 and 2.5 need to be complete to proceed!\n\n3. Once [CI checks after merge to master are complete](https://github.com/eclipse-theia/theia-ide/actions), trigger the Jenkins Release Preview <https://ci.eclipse.org/theia/job/theia-ide-release/> job without parameters.\n\n4. Once 3. is successful the notarize job <https://ci.eclipse.org/theia/job/theia-ide-sign-notarize/> is started automatically.\n\n5. Once 4. is successful it starts the upload job <https://ci.eclipse.org/theia/job/theia-ide-upload/>\n\n  *Note*: Please report if upload fails more than 5 times, we need to investigate!\n\n## 3. Preview, Testing, and Release Process\n<!-- release: both -->\n\nOnce the PR is merged and the preview build is created, follow these steps for testing and eventual release:\n\n### 3.1 Confirm the new preview version is published (do not promote as stable yet)\n<!-- release: both -->\n\n- Check if uploaded versions are complete in in download folder <https://download.eclipse.org/theia/ide-preview/{{ideVersion}}/>\n  - e.g. check if the latest files are correct and the artifacts are there (in doubt, compare to previous versions)\n\n### 3.2 Announce Preview Test Phase\n<!-- release: minor -->\n\n### 3.2.1 GH Discussions\n<!-- release: minor -->\n\n- Use GitHub Discussions for the announcement in the [Category Release Announcements](https://github.com/eclipse-theia/theia/discussions/new?category=release-announcements).\n\n   Title:\n\n   ```md\n   Theia IDE {{majorMinor}}.x Preview Testing\n   ```\n\n   Body:\n\n   ```md\n   The new version {{ideVersion}} of the Theia IDE is available on the preview channel now. Please join the preview testing!\n\n   You can download it here:\n\n   - [Linux](https://download.eclipse.org/theia/ide-preview/{{ideVersion}}/linux/TheiaIDE.AppImage)\n   - [Mac x86](https://download.eclipse.org/theia/ide-preview/{{ideVersion}}/macos/TheiaIDE.dmg)\n   - [Mac ARM](https://download.eclipse.org/theia/ide-preview/{{ideVersion}}/macos-arm/TheiaIDE.dmg)\n   - [Windows](https://download.eclipse.org/theia/ide-preview/{{ideVersion}}/windows/TheiaIDESetup.exe)\n\n   Update your existing installation by setting the preference `updates.channel` to `preview`.\n\n   Please respond here when you can test the preview without finding blockers, by commenting with a ✔️.\n   If you find any issues, please mention them in this thread and report them as an issue once confirmed by other testers.\n\n   | Phase | Target | Status |\n   |:---|:---|:---|\n   | {{ideVersion}} preview available | {{releaseDate}} | :white_check_mark: |\n   | {{ideVersion}} community preview window | {{previewStart}} → {{previewEnd}} | :hourglass_flowing_sand: |\n   | Theia IDE {{majorMinor}}.x Promoted to Stable | {{previewEnd}} + 1 business day |  |\n   | Docker image Publish | {{previewEnd}} + 1 business day |  |\n   | Snap updated | {{previewEnd}} + 1 business day |  |\n   ```\n\n- Pin discussion to the Release Announcement Category\n\n### 3.2.2 theia-dev mailing list\n<!-- release: minor -->\n\n- Announce the preview release via email to [the `theia-dev` mailing List](mailto:theia-dev@eclipse.org) with the following template:\n\n   Subject:\n\n   ```md\n   Theia IDE {{majorMinor}}.x preview phase\n   ```\n\n   Body:\n\n   ```md\n   Hi everyone,\n\n   Version {{ideVersion}} of the Theia IDE is now available on the preview channel. Please join the preview test and help us stabilize the release.\n   Visit the preview discussion for more information and coordination: https://github.com/eclipse-theia/theia/discussions/{{discussionNumber}}\n   ```\n\n### 3.2.3 Eclipse Theia Release discussion\n<!-- release: minor -->\n\n- Announce the start of the Theia IDE Preview Test phase in the Theia Release announcement (`Eclipse Theia v{{version}}`) discussion (see <https://github.com/eclipse-theia/theia/discussions/categories/release-announcements>):\n\n   ```md\n   The preview test phase for the Theia IDE {{ideVersion}} has started. You can find the details here: \n   \n   - https://github.com/eclipse-theia/theia/discussions/{{discussionNumber}}\n   ```\n\n### 3.2.4 Optional: Announcement to Theia IDE Preview Testers\n<!-- release: minor -->\n\n- Optional: Announce the start of the Theia IDE Preview Test phase to your testers (e.g., via Slack, Teams, E-Mail):\n\n   ```md\n   :theia: The Theia IDE preview {{ideVersion}} for Theia version {{version}} is now available!\n   Please take a moment to test it and provide feedback - whether you've run into issues or everything works as expected. {{linkToNewPreviewComment}}\n\n   To help us get a quick overview, please react with the emoji for your OS (:ubuntu:, :windows:, :mac_arm:, :mac_x64:) once you updated to the new version.\n   Thanks!\n   ```\n\n### 3.3 Patch Releases\n<!-- release: patch -->\n\n- Address reported blockers and issue patch releases (this process may take 1–2 weeks).\n\n   **Note:** If issues are persistent, or resources are insufficient, the release may be postponed to the next version.\n\n- If a blocker was found add this status to the respective preview window:\n\n   ```md\n    :no_entry:  blocker was found \n   ```\n\n### 3.3.1 Update Preview Discussion with new preview\n<!-- release: patch -->\n\n- For Patch Releases, use the [Preview discussion](#32-announce-preview-test-phase) and post a comment to announce the patch release of the Theia IDE: <https://github.com/eclipse-theia/theia/discussions/{{discussionNumber}}>\n\n   ```md\n   The new version {{ideVersion}} of the Theia IDE is available on the preview channel now. Please join the preview testing!\n\n   You can download it here:\n\n   - [Linux](https://download.eclipse.org/theia/ide-preview/{{ideVersion}}/linux/TheiaIDE.AppImage)\n   - [Mac x86](https://download.eclipse.org/theia/ide-preview/{{ideVersion}}/macos/TheiaIDE.dmg)\n   - [Mac ARM](https://download.eclipse.org/theia/ide-preview/{{ideVersion}}/macos-arm/TheiaIDE.dmg)\n   - [Windows](https://download.eclipse.org/theia/ide-preview/{{ideVersion}}/windows/TheiaIDESetup.exe)\n\n   Update your existing installation by setting the preference `updates.channel` to `preview`.\n\n   Please respond here when you can test the preview without finding blockers, by commenting with a ✔️.\n   If you find any issues, please mention them in this thread and report them as an issue once confirmed by other testers.\n   ```\n\n### 3.3.2 Update Preview Discussion Table\n<!-- release: patch -->\n\n- Adapt the target dates to the planned new preview window and final release dates\n- Add comments in the status if necessary\n- See example announcements here: <https://github.com/eclipse-theia/theia/discussions/16109>\n\n- Update the discussion's status table with two rows for the current patch release:\n\n   ```md\n   | [{{ideVersion}} preview available]({{linkToNewPreviewComment}}) | {{previewStart}} | :white_check_mark: |\n   | {{ideVersion}} community preview window | {{previewStart}} → {{previewEnd}} | :hourglass_flowing_sand: |\n   ```\n\n- Also adapt the target dates of the remaining entries in table (if not yet promoted)\n\n### 3.3.3 Optional: Internal Slack announcement\n<!-- release: patch -->\n\n- Optional: Announce the start of the Theia IDE Preview Test phase in your internal Slack channel:\n\n   ```md\n   :theia: The Theia IDE preview {{ideVersion}} for Theia version {{version}} is now available!\n   Please take a moment to test it and provide feedback - whether you've run into issues or everything works as expected. {{linkToNewPreviewComment}}\n\n   To help us get a quick overview, please react with the emoji for your OS (:ubuntu:, :windows:, :mac_arm:, :mac_x64:) once you updated to the new version.\n   Thanks!\n   ```\n\n## 3.4 Optional: Internal Slack - feedback reminder\n<!-- release: both -->\n\n- Optional: Schedule a slack reminder close to the preview end:\n\n```md\nQuick reminder: We're planning to promote the new IDE version (e.g., tomorrow/on Monday), please share your feedback for the preview {{ideVersion}}!\n:point_right: https://github.com/eclipse-theia/theia/discussions/{{discussionNumber}}\nThanks! :thx:\n```\n\n## 4. Promote IDE from Preview to Stable Channel\n<!-- release: both -->\n\nPromote the IDE using the [Build Job](https://ci.eclipse.org/theia/job/Theia%20-%20Promote%20IDE/).\n\n- Specify the release version in the `VERSION` parameter (e.g., 1.48.0), corresponding to the **THEIA_IDE_VERSION** copied from <https://download.eclipse.org/theia/ide-preview/>.\n\n- Post a comment to announce the official release of the Theia IDE:\n\n   ```md\n   {{ideVersion}} has been promoted to stable\n   ```\n\n  - Update the [Base Preview discussion](#32-announce-preview-test-phase) status table with a checkmark and the version that has been published.\n\n   ```md\n   | [Theia IDE {{majorMinor}}.x Promoted to Stable](https://download.eclipse.org/theia/ide/1.67.100/) | {{today}} | :white_check_mark: |\n   ```\n\n  - Mark the message as the answer.\n\n  - Unpin discussion from the Release Announcement Category.\n\n## 5. Tag the Release Commit\n<!-- release: both -->\n\nAfter promoting the release, tag the release commit as follows:\n\n1. Create the tag:\n\n   ```bash\n   git tag v{{ideVersion}} ${SHA of release commit}\n   ```\n\n2. Push the tag to the repository:\n\n   ```bash\n   git push origin v{{ideVersion}}\n   ```\n\n## 6. Publish Docker Image\n<!-- release: both -->\n\nPublish the Docker image by running the [workflow](https://github.com/eclipse-theia/theia-ide/actions/workflows/publish-theia-ide-img.yml) from the `master` branch. Use **${THEIA_IDE_VERSION}** as the version.\n(We do NOT use the v prefix here in this case currently).\n\n- Check the GH package page if the image was published correctly: <https://github.com/eclipse-theia/theia-ide/pkgs/container/theia-ide%2Ftheia-ide>\n\n- Update the [Preview discussion](#32-announce-preview-test-phase) status table\n\n   ```md\n   | [Docker image Publish](https://github.com/eclipse-theia/theia-ide/pkgs/container/theia-ide%2Ftheia-ide) | {{today}} | :white_check_mark: |\n   ```\n\n## 7. Snap Update\n<!-- release: both -->\n\nCan be parallel to step 6.\nAfter the IDE is promoted to stable, perform these steps for the snap update:\n\n1. Run [this workflow](https://github.com/eclipse-theia/theia-ide-snap/actions/workflows/update.yml) from the `master` branch.\n2. After the build succeeded, visit <https://github.com/eclipse-theia/theia-ide-snap/pulls> to find the PR that updates to **${THEIA_IDE_VERSION}**.\n3. Check out the corresponding branch.\n4. Amend the latest commit with your author details:\n\n   ```bash\n   git commit --amend --author=\"Your Name <name@example.com>\"\n   ```\n\n5. Force push the branch.\n6. Verify that all checks pass, and then `rebase and merge`.\n7. Confirm the master branch build (Store Publishing) is successful.\n8. Check if snap is available <https://snapcraft.io/theia-ide>\n9. Update the [Preview discussion](#32-announce-preview-test-phase) status table\n\n   ```md\n   | [Snap updated](https://snapcraft.io/theia-ide) | {{today}} | :white_check_mark: |\n   ```\n\n## 8. Upgrade Dependencies\n<!-- release: both -->\n\nAfter each release, run the following command to upgrade dependencies:\n\nKeep this upgrade process in a separate PR, as it might require IP Reviews from the Eclipse Foundation and additional time. Also, verify the `electron` version in `yarn.lock` and adjust `electronVersion` in `applications/electron/electron-builder.yml` if it has changed.\n\nTo perform the upgrade:\n\n- Run `yarn upgrade` at the root of the repository.\n- Fix any compilation errors, typing errors, and failing tests.\n- Open a PR with the changes ([example](https://github.com/eclipse-theia/theia-ide/pull/568)).\n- The license check review is done via the CI.\n- Wait for the \"IP Check\" to complete ([example](https://gitlab.eclipse.org/eclipsefdn/emo-team/iplab/-/issues/22828)).\n\nPerforming this after the release helps us to find issues with the new dependencies and gives time to perform a license check on the dependencies.\n"
  },
  {
    "path": "README.md",
    "content": "<br/>\n<div id=\"theia-logo\" align=\"center\">\n    <br />\n    <img src=\"https://raw.githubusercontent.com/eclipse-theia/theia-ide/master/theia-extensions/product/src/browser/icons/TheiaIDE.png\" alt=\"Theia Logo\" width=\"300\"/>\n    <h3>Eclipse Theia IDE</h3>\n</div>\n\n<div id=\"badges\" align=\"center\">\n\nThe Eclipse Theia IDE is built with this project.\\\nEclipse Theia IDE also serves as a template for building desktop-based products based on the Eclipse Theia platform.\n\n</div>\n\n[![Installers](https://img.shields.io/badge/download-installers-blue.svg?style=flat-curved)](https://theia-ide.org//#theiaidedownload)\n[![Build Status](https://ci.eclipse.org/theia/buildStatus/icon?subject=latest&job=Theia2%2Fmaster)](https://ci.eclipse.org/theia/job/Theia2/job/master/)\n<!-- currently we have no working next job because next builds are not published -->\n<!-- [![Build Status](https://ci.eclipse.org/theia/buildStatus/icon?subject=next&job=theia-next%2Fmaster)](https://ci.eclipse.org/theia/job/theia-next/job/master/) -->\n\n[Main Theia Repository](https://github.com/eclipse-theia/theia)\n\n[Visit the Theia website](http://www.theia-ide.org) for more documentation: [Using the Theia IDE](https://theia-ide.org/docs/user_getting_started/), [Packaging Theia as a Desktop Product](https://theia-ide.org/docs/blueprint_documentation/).\n\n## License\n\n- [MIT](LICENSE)\n\n## Trademark\n\n\"Theia\" is a trademark of the Eclipse Foundation\n<https://www.eclipse.org/theia>\n\n## What is this?\n\nThe Eclipse IDE is a modern and open IDE for cloud and desktop. The Theia IDE is based on the [Theia platform](https://theia-ide.org).\nThe Theia IDE is available as a [downloadable desktop application](https://theia-ide.org//#theiaidedownload). You can also try the latest version of the Theia IDE online. The online test version is limited to 30 minutes per session and hosted via Theia.cloud. Finally, we provide an [experimental Docker image](#docker) for hosting the Theia IDE online.\n\nThe Eclipse Theia IDE also serves as a **template** for building desktop-based products based on the Eclipse Theia platform, as well as to showcase Eclipse Theia capabilities. It is made up of a subset of existing Eclipse Theia features and extensions. [Documentation is available](https://theia-ide.org/docs/composing_applications/) to help you customize and build your own Eclipse Theia-based product.\n\n## Theia IDE vs Theia Blueprint\n\nThe Theia IDE has been rebranded from its original name “Theia Blueprint”. You can therefore assume the terms “Theia IDE” and “Theia Blueprint” to be synonymous.\n\n## Development\n\n### Requirements\n\nPlease check Theia's [prerequisites](https://github.com/eclipse-theia/theia/blob/master/doc/Developing.md#prerequisites), and keep node versions aligned between Theia IDE and that of the referenced Theia version.\n\n### Documentation\n\nDocumentation on how to package Theia as a Desktop Product may be found [here](https://theia-ide.org/docs/blueprint_documentation/)\n\nFor adopters building their own products based on this template, see the [Adopter Guide](ADOPTER.md) for additional considerations.\n\n### Repository Structure\n\n- Root level configures mono-repo build with lerna\n- `applications` groups the different app targets\n  - `browser` contains a browser based version of Eclipse Theia IDE that may be packaged as a Docker image\n  - `electron` contains the electron app to package, packaging configuration, and E2E tests for the electron target.\n- `theia-extensions` groups the various custom theia extensions for the Eclipse Theia IDE\n  - `product` contains a Theia extension contributing the product branding (about dialogue and welcome page).\n  - `updater` contains a Theia extension contributing the update mechanism and corresponding UI elements (based on the electron updater).\n  - `launcher` contains a Theia extension contributing, for AppImage applications, the option to create a script that allows to start the Eclipse Theia IDE from the command line by calling the 'theia' command.\n- `patches` contains patches applied to upstream packages\n\n### Build\n\nFor development and casual testing of the Eclipse Theia IDE, one can build it in \"dev\" mode. This permits building the IDE on systems with less resources, like a Raspberry Pi 4B with 4GB of RAM.\n\nNOTE: If manually building after updating dependencies or pulling to a newer commit, run `git clean -xfd` to help avoid runtime conflicts.\n\n```sh\n# Build \"dev\" version of the app. Its quicker, uses less resources, \n# but the front end app is not \"minified\"\nyarn && yarn build:dev && yarn download:plugins\n```\n\nProduction applications:\n\n```sh\n# Build production version of the Eclipse Theia IDE app\nyarn && yarn build && yarn download:plugins\n```\n\n### Package the Applications\n\nATM we only produce packages for the Electron application.\n\n_If you are trying to compile for arm on an arm machine, you may want to follow [these steps](https://github.com/eclipse-theia/theia-ide/issues/690#issuecomment-4157768849) before_\n\n```sh\nyarn package:applications\n# or\nyarn electron package\n```\n\nThe packaged application is located in `applications/electron/dist`.\n\n### Create a Preview Electron Electron Application (without packaging it)\n\n```sh\nyarn electron package:preview\n```\n\nThe packaged application is located in `applications/electron/dist`.\n\n### Running E2E Tests on Electron\n\nThe E2E tests basic UI tests of the actual application.\nThis is done based on the preview of the packaged application.\n\n```sh\nyarn electron package:preview\nyarn electron test\n```\n\n### Running Browser app\n\nThe browser app may be started with\n\n```sh\nyarn browser start\n```\n\nand connect to <http://localhost:3000/>\n\n### Developing with Local Theia Framework\n\nTo build and test the Theia IDE against a local development version of the Theia framework, see [docs/developing-with-local-theia.md](docs/developing-with-local-theia.md).\n\n### Troubleshooting\n\n- [_\"Don't expect that you can build app for all platforms on one platform.\"_](https://www.electron.build/multi-platform-build)\n\n### Reporting Feature Requests and Bugs\n\nThe features in the Eclipse Theia IDE are based on Theia and the included extensions/plugins. For bugs in Theia please consider opening an issue in the [Theia project on Github](https://github.com/eclipse-theia/theia/issues/new/choose).\nThe Eclipse Theia IDE only packages existing functionality into a product and installers for the product. If you believe there is a mistake in packaging, something needs to be added to the packaging or the installers do not work properly, please [open an issue on Github](https://github.com/eclipse-theia/theia-ide/issues/new/choose) to let us know.\n\n### Docker\n\nThe Docker image of the Theia IDE is currently in _experimental state_. It is built from the same sources and packages as the desktop version, but it is not part of the [preview test](https://github.com/eclipse-theia/theia-ide/blob/master/PUBLISHING.md#preview-testing-and-release-process-for-the-theia-ide).\nYou can find a prebuilt Docker image of the IDE [here](https://github.com/eclipse-theia/theia-ide/pkgs/container/theia-ide%2Ftheia-ide).\n\nYou can also create the Docker image for the Eclipse Theia IDE based on the browser app with the following build command:\n\n```sh\ndocker build -t theia-ide -f browser.Dockerfile .\n```\n\nYou may then run this with\n\n```sh\ndocker run -p=3000:3000 --rm theia-ide\n```\n\nand connect to <http://localhost:3000/>\n"
  },
  {
    "path": "applications/browser/package.json",
    "content": "{\n  \"private\": true,\n  \"name\": \"theia-ide-browser-app\",\n  \"description\": \"Eclipse Theia IDE browser product\",\n  \"productName\": \"Theia IDE\",\n  \"version\": \"1.71.100\",\n  \"license\": \"MIT\",\n  \"author\": \"Eclipse Theia <theia-dev@eclipse.org>\",\n  \"homepage\": \"https://github.com/eclipse-theia/theia-ide#readme\",\n  \"bugs\": {\n    \"url\": \"https://github.com/eclipse-theia/theia/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/eclipse-theia/theia-ide.git\"\n  },\n  \"engines\": {\n    \"yarn\": \">=1.7.0 <2\",\n    \"node\": \">=22\"\n  },\n  \"theia\": {\n    \"frontend\": {\n      \"config\": {\n        \"applicationName\": \"Theia IDE\",\n        \"warnOnPotentiallyInsecureHostPattern\": false,\n        \"preferences\": {\n          \"toolbar.showToolbar\": true,\n          \"files.enableTrash\": false,\n          \"security.workspace.trust.enabled\": false\n        },\n        \"reloadOnReconnect\": true\n      }\n    },\n    \"backend\": {\n      \"config\": {\n        \"warnOnPotentiallyInsecureHostPattern\": false,\n        \"startupTimeout\": -1,\n        \"resolveSystemPlugins\": false,\n        \"configurationFolder\": \".theia-ide\",\n        \"frontendConnectionTimeout\": 3000\n      }\n    },\n    \"generator\": {\n      \"config\": {\n        \"preloadTemplate\": \"./resources/preload.html\"\n      }\n    }\n  },\n  \"dependencies\": {\n    \"@theia/ai-anthropic\": \"1.72.0-next.20\",\n    \"@theia/ai-chat\": \"1.72.0-next.20\",\n    \"@theia/ai-chat-ui\": \"1.72.0-next.20\",\n    \"@theia/ai-claude-code\": \"1.72.0-next.20\",\n    \"@theia/ai-code-completion\": \"1.72.0-next.20\",\n    \"@theia/ai-codex\": \"1.72.0-next.20\",\n    \"@theia/ai-copilot\": \"1.72.0-next.20\",\n    \"@theia/ai-core\": \"1.72.0-next.20\",\n    \"@theia/ai-core-ui\": \"1.72.0-next.20\",\n    \"@theia/ai-editor\": \"1.72.0-next.20\",\n    \"@theia/ai-google\": \"1.72.0-next.20\",\n    \"@theia/ai-history\": \"1.72.0-next.20\",\n    \"@theia/ai-huggingface\": \"1.72.0-next.20\",\n    \"@theia/ai-ide\": \"1.72.0-next.20\",\n    \"@theia/ai-llamafile\": \"1.72.0-next.20\",\n    \"@theia/ai-mcp\": \"1.72.0-next.20\",\n    \"@theia/ai-mcp-server\": \"1.72.0-next.20\",\n    \"@theia/ai-mcp-ui\": \"1.72.0-next.20\",\n    \"@theia/ai-ollama\": \"1.72.0-next.20\",\n    \"@theia/ai-openai\": \"1.72.0-next.20\",\n    \"@theia/ai-scanoss\": \"1.72.0-next.20\",\n    \"@theia/ai-terminal\": \"1.72.0-next.20\",\n    \"@theia/ai-vercel-ai\": \"1.72.0-next.20\",\n    \"@theia/bulk-edit\": \"1.72.0-next.20\",\n    \"@theia/callhierarchy\": \"1.72.0-next.20\",\n    \"@theia/collaboration\": \"1.72.0-next.20\",\n    \"@theia/console\": \"1.72.0-next.20\",\n    \"@theia/core\": \"1.72.0-next.20\",\n    \"@theia/debug\": \"1.72.0-next.20\",\n    \"@theia/dev-container\": \"1.72.0-next.20\",\n    \"@theia/editor\": \"1.72.0-next.20\",\n    \"@theia/editor-preview\": \"1.72.0-next.20\",\n    \"@theia/external-terminal\": \"1.72.0-next.20\",\n    \"@theia/file-search\": \"1.72.0-next.20\",\n    \"@theia/filesystem\": \"1.72.0-next.20\",\n    \"@theia/getting-started\": \"1.72.0-next.20\",\n    \"@theia/keymaps\": \"1.72.0-next.20\",\n    \"@theia/markers\": \"1.72.0-next.20\",\n    \"@theia/memory-inspector\": \"1.72.0-next.20\",\n    \"@theia/messages\": \"1.72.0-next.20\",\n    \"@theia/metrics\": \"1.72.0-next.20\",\n    \"@theia/mini-browser\": \"1.72.0-next.20\",\n    \"@theia/monaco\": \"1.72.0-next.20\",\n    \"@theia/navigator\": \"1.72.0-next.20\",\n    \"@theia/notebook\": \"1.72.0-next.20\",\n    \"@theia/outline-view\": \"1.72.0-next.20\",\n    \"@theia/output\": \"1.72.0-next.20\",\n    \"@theia/plugin-dev\": \"1.72.0-next.20\",\n    \"@theia/plugin-ext\": \"1.72.0-next.20\",\n    \"@theia/plugin-ext-vscode\": \"1.72.0-next.20\",\n    \"@theia/preferences\": \"1.72.0-next.20\",\n    \"@theia/preview\": \"1.72.0-next.20\",\n    \"@theia/process\": \"1.72.0-next.20\",\n    \"@theia/property-view\": \"1.72.0-next.20\",\n    \"@theia/remote\": \"1.72.0-next.20\",\n    \"@theia/scanoss\": \"1.72.0-next.20\",\n    \"@theia/scm\": \"1.72.0-next.20\",\n    \"@theia/search-in-workspace\": \"1.72.0-next.20\",\n    \"@theia/secondary-window\": \"1.72.0-next.20\",\n    \"@theia/task\": \"1.72.0-next.20\",\n    \"@theia/terminal\": \"1.72.0-next.20\",\n    \"@theia/terminal-manager\": \"1.72.0-next.20\",\n    \"@theia/test\": \"1.72.0-next.20\",\n    \"@theia/timeline\": \"1.72.0-next.20\",\n    \"@theia/toolbar\": \"1.72.0-next.20\",\n    \"@theia/typehierarchy\": \"1.72.0-next.20\",\n    \"@theia/userstorage\": \"1.72.0-next.20\",\n    \"@theia/variable-resolver\": \"1.72.0-next.20\",\n    \"@theia/vsx-registry\": \"1.72.0-next.20\",\n    \"@theia/workspace\": \"1.72.0-next.20\",\n    \"fs-extra\": \"^9.1.0\",\n    \"theia-ide-product-ext\": \"1.71.100\"\n  },\n  \"devDependencies\": {\n    \"@theia/cli\": \"1.72.0-next.20\",\n    \"@theia/bundle-plugin\": \"1.72.0-next.20\"\n  },\n  \"scripts\": {\n    \"clean\": \"theia clean && rimraf node_modules\",\n    \"build\": \"yarn -s rebuild && theia build --app-target=\\\"browser\\\" --mode development\",\n    \"build:prod\": \"yarn -s rebuild && theia  build --app-target=\\\"browser\\\"\",\n    \"rebuild\": \"theia rebuild:browser --cacheRoot ../..\",\n    \"start\": \"theia start --plugins=local-dir:../../plugins\",\n    \"watch\": \"concurrently --kill-others -n tsc,build -c red,yellow \\\"tsc -b -w --preserveWatchOutput\\\" \\\"yarn -s watch:bundle\\\"\",\n    \"update:theia\": \"ts-node ../../scripts/update-theia-version.ts\",\n    \"update:next\": \"ts-node ../../scripts/update-theia-version.ts next\"\n  }\n}"
  },
  {
    "path": "applications/browser/resources/preload.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <style>\n        html,\n        body {\n            background-color: black;\n        }\n\n        .theia-preload {\n            position: absolute;\n            top: 0;\n            left: 0;\n            right: 0;\n            bottom: 0;\n            /* Above styles copied from https://github.com/eclipse-theia/theia/blob/5aeef6c0c683b4e91713ab736957e6655b486adc/packages/core/src/browser/style/index.css#L147-L151 */\n            /* Otherwise, there is a flickering when Theia's CSS loads. */\n\n            background-image: none;\n        }\n\n        .theia-preload::after {\n            /* remove default loading animation */\n            content: none;\n        }\n\n        .spinner-container {\n            display: flex;\n            flex-direction: center;\n            align-self: center;\n            justify-content: center;\n            height: 100vh;\n            width: 100vw;\n        }\n\n        .custom-spinner {\n            align-self: center;\n        }\n\n        .custom-spinner svg {\n            width: 16vw;\n            height: 16vh;\n            animation-delay: 0;\n            animation-duration: 2s;\n            animation-iteration-count: infinite;\n            animation-name: theia-ide-spinner;\n            animation-timing-function: ease;\n        }\n\n        @keyframes theia-ide-spinner {\n            0% {\n                filter: invert(49%) sepia(71%) saturate(5980%) hue-rotate(199deg) brightness(103%) contrast(101%);\n                transform: scale(1.0);\n            }\n\n            50% {\n                filter: invert(57%) sepia(52%) saturate(1900%) hue-rotate(160deg) brightness(100%) contrast(102%);\n                transform: scale(0.8);\n            }\n\n            100% {\n                filter: invert(49%) sepia(71%) saturate(5980%) hue-rotate(199deg) brightness(103%) contrast(101%);\n                transform: scale(1.0);\n            }\n        }\n    </style>\n</head>\n\n<body>\n    <div class='spinner-container'>\n        <div class='custom-spinner'>\n            <svg id=\"spinner\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"\n                xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0\" y=\"0\" preserveAspectRatio=\"xMinYMin meet\"\n                viewBox=\"0, 0, 1150, 540.6\">\n                <g id=\"Layer_1\" fill=\"#FFFFFF\">\n                    <path\n                        d=\"M880.199,2.8 C1028.1,2.8 1147.9,122.6 1147.9,270.5 C1147.9,418.3 1028.1,538.2 880.2,538.2 L290.1,538.2 C269,538.2 251.9,521.1 251.9,500 C251.9,478.9 269,461.8 290.1,461.8 L427.6,461.8 C448.6,461.8 465.7,444.7 465.7,423.6 C465.7,402.5 448.6,385.4 427.6,385.4 L396.999,385.4 C375.9,385.4 358.8,368.3 358.8,347.2 C358.8,326.1 375.9,309 397,309 L488.703,309 C509.918,308.941 526.373,291.65 526.9,270.8 C526.9,249.7 509.8,232.6 488.7,232.6 L167.8,232.6 C146.7,232.6 129.6,215.5 129.6,194.4 C129.6,173.3 146.7,156.2 167.8,156.2 L404.604,156.2 C425.818,156.141 442.273,138.85 442.8,118 C442.8,96.9 425.7,79.8 404.6,79.8 L351.2,79.8 C330.1,79.8 313,62.7 313,41.6 C313,20.5 330.1,2.4 351.2,2.4 L880.199,2.8 z M837.4,92 L837.4,92 C755.2,92 688.7,158.6 688.7,240.7 L688.7,300.2 C688.7,382.4 755.2,448.9 837.4,448.9 C919.5,448.9 986.1,382.4 986.1,300.2 L986.1,240.7 C986.1,158.6 919.5,92 837.4,92 L837.4,92 z M888.2,232.6 C908,232.6 924.1,248.7 924.1,268.5 L924.1,273.1 C924.1,292.9 908,309 888.2,309 L776.6,309 C756.8,309 740.7,292.9 740.7,273.1 L740.7,268.5 C740.7,248.7 756.8,232.6 776.6,232.6 L888.2,232.6 z\" />\n                    <path\n                        d=\"M170.1,461.8 C190,461.8 206,477.8 206,497.7 L206,502.3 C206,522.1 190,538.2 170.1,538.2 L38,538.2 C18.2,538.2 2.1,522.1 2.1,502.3 L2.1,497.7 C2.1,477.8 18.2,461.8 38,461.8 L170.1,461.8 z\" />\n                    <path\n                        d=\"M231.3,3.4 C251.1,3.4 267.1,19.5 267.1,39.3 L267.1,44 C267.1,63.8 251.1,79.8 231.3,79.8 L83.8,79.8 C64,79.8 47.9,63.8 47.9,44 L47.9,39.3 C47.9,19.5 64,3.4 83.8,3.4 L231.3,3.4 z\" />\n                    <path\n                        d=\"M277.1,309 C296.9,309 313,325.1 313,344.9 L313,349.5 C313,369.3 296.9,385.4 277.1,385.4 L196.1,385.4 C176.3,385.4 160.2,369.3 160.2,349.5 L160.2,344.9 C160.2,325.1 176.3,309 196.1,309 L277.1,309 z\" />\n                </g>\n            </svg>\n        </div>\n    </div>\n\n    <script>\n        if (document.head) {\n            let link = document.createElement('link');\n            link.rel = 'icon';\n            link.href = '/favicon.ico';\n            document.head.appendChild(link);\n        }\n    </script>\n</body>\n\n</html>"
  },
  {
    "path": "applications/browser/tsconfig.json",
    "content": "{\n    \"extends\": \"../../configs/base.tsconfig\",\n    \"include\": [],\n    \"compilerOptions\": {\n      \"composite\": true\n    },\n    \"references\": [\n      {\n        \"path\": \"../../theia-extensions/launcher\"\n      },\n      {\n        \"path\": \"../../theia-extensions/product\"\n      }\n    ]\n}\n"
  },
  {
    "path": "applications/browser/webpack.config.js",
    "content": "/**\n * This file can be edited to customize webpack configuration.\n * To reset delete this file and rerun theia build again.\n */\n// @ts-check\nconst configs = require('./gen-webpack.config.js');\nconst nodeConfig = require('./gen-webpack.node.config.js');\nconst path = require('path');\nconst CopyWebpackPlugin = require('copy-webpack-plugin');\n\n/**\n * Expose bundled modules on window.theia.moduleName namespace, e.g.\n * window['theia']['@theia/core/lib/common/uri'].\n * Such syntax can be used by external code, for instance, for testing.\nconfigs[0].module.rules.push({\n    test: /\\.js$/,\n    loader: require.resolve('@theia/application-manager/lib/expose-loader')\n}); */\n\n// serve favico from root\n// @ts-ignore\nconfigs[0].plugins.push(\n    // @ts-ignore\n    new CopyWebpackPlugin({\n        patterns: [\n            {\n                context: path.resolve('.', '..', '..', 'applications', 'browser', 'ico'),\n                from: '**'\n            }\n        ]\n    })\n);\n\nmodule.exports = [\n    ...configs,\n    nodeConfig.config\n];"
  },
  {
    "path": "applications/electron/.eslintrc.js",
    "content": "/** @type {import('eslint').Linter.Config} */\nmodule.exports = {\n    extends: [\n        '../../configs/build.eslintrc.json'\n    ],\n    parserOptions: {\n        tsconfigRootDir: __dirname,\n        project: 'tsconfig.eslint.json'\n    }\n};\n"
  },
  {
    "path": "applications/electron/electron-builder.yml",
    "content": "appId: eclipse.theia\nproductName: TheiaIDE\ncopyright: Copyright © 2020-2025 Eclipse Foundation, Inc\nelectronDist: ../../node_modules/electron/dist\nelectronVersion: 39.8.7\nasar: true\nasarUnpack:\n  - \"**/lib/backend/native/**\"\n  - \"**/lib/backend/shell-integrations/**\"\n  - \"**/lib/build/Release/**\"\n  - \"**/lib/prebuilds/**\"\nnodeGypRebuild: false\nnpmRebuild: false\n\ndirectories:\n  buildResources: resources\n\n# node_modules and package.json are copied automatically\n# Exclude node_modules manually because electron is copied by electron-builder and we are using a bundled backend\nfiles:\n  - src-gen\n  - lib\n  - resources/icons/WindowIcon/512-512.png\n  - resources/TheiaIDESplash.svg\n  - scripts\n  - \"!**node_modules/**\"\nextraResources:\n  - from: ../../plugins\n    to: app/plugins\n\nwin:\n  icon: resources/icons/WindowsLauncherIcons/TheiaIDE.ico\n  target:\n    - nsis\n  publish:\n    provider: generic\n    url: \"https://download.eclipse.org/theia/ide/${version}/windows\"\n    useMultipleRangeRequest: false\nmac:\n  icon: resources/icons/MacLauncherIcons/icon.icns\n  category: public.app-category.developer-tools\n  protocols:\n    - name: theia\n      schemes:\n        - theia\n  darkModeSupport: true\n  target:\n    - dmg\n    - zip\n  publish:\n    provider: generic\n    url: \"https://download.eclipse.org/theia/ide/latest/macos\"\nlinux:\n  icon: resources/icons/LinuxLauncherIcons\n  category: Development\n  mimeTypes:\n    - inode/directory\n  vendor: Eclipse Foundation, Inc\n  target:\n    - deb\n    - AppImage\n  publish:\n    provider: generic\n    url: \"https://download.eclipse.org/theia/ide/latest/linux\"\n\nnsis:\n  menuCategory: true\n  oneClick: false\n  perMachine: false\n  installerHeaderIcon: resources/icons/WindowsLauncherIcons/TheiaIDE.ico\n  installerIcon: resources/icons/WindowsLauncherIcons/TheiaIDE.ico\n  uninstallerIcon: resources/icons/WindowsLauncherIcons/TheiaIDE.ico\n  installerSidebar: resources/icons/InstallerSidebarImage/164-314Windows.bmp\n  uninstallerSidebar: resources/icons/InstallerSidebarImage/164-314Windows.bmp\n  allowToChangeInstallationDirectory: true\n  runAfterFinish: false\n  artifactName: ${productName}Setup.${ext}\n  license: LICENSE\ndmg:\n  artifactName: ${productName}.${ext}\ndeb:\n  artifactName: ${productName}.${ext}\nappImage:\n  artifactName: ${productName}.${ext}\n\nafterPack: ./scripts/after-pack.js\n"
  },
  {
    "path": "applications/electron/entitlements.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n  <dict>\n    <key>com.apple.security.cs.allow-jit</key>\n    <true/>\n    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>\n    <true/>\n    <key>com.apple.security.cs.allow-dyld-environment-variables</key>\n    <true/>\n    <key>com.apple.security.cs.disable-library-validation</key>\n    <true/>\n  </dict>\n</plist>\n"
  },
  {
    "path": "applications/electron/package.json",
    "content": "{\n  \"private\": true,\n  \"name\": \"theia-ide-electron-app\",\n  \"description\": \"Eclipse Theia IDE product\",\n  \"productName\": \"Theia IDE\",\n  \"version\": \"1.71.100\",\n  \"main\": \"scripts/theia-electron-main.js\",\n  \"license\": \"MIT\",\n  \"author\": \"Eclipse Theia <theia-dev@eclipse.org>\",\n  \"homepage\": \"https://github.com/eclipse-theia/theia-ide#readme\",\n  \"bugs\": {\n    \"url\": \"https://github.com/eclipse-theia/theia/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/eclipse-theia/theia-ide.git\"\n  },\n  \"engines\": {\n    \"yarn\": \">=1.7.0 <2\",\n    \"node\": \">=22\"\n  },\n  \"theia\": {\n    \"target\": \"electron\",\n    \"frontend\": {\n      \"config\": {\n        \"applicationName\": \"Theia IDE\",\n        \"reloadOnReconnect\": true,\n        \"preferences\": {\n          \"toolbar.showToolbar\": true\n        },\n        \"electron\": {\n          \"showWindowEarly\": false,\n          \"splashScreenOptions\": {\n            \"content\": \"resources/TheiaIDESplash.svg\",\n            \"height\": 276,\n            \"width\": 446\n          }\n        }\n      }\n    },\n    \"backend\": {\n      \"config\": {\n        \"frontendConnectionTimeout\": -1,\n        \"startupTimeout\": -1,\n        \"resolveSystemPlugins\": false,\n        \"configurationFolder\": \".theia-ide\"\n      }\n    },\n    \"generator\": {\n      \"config\": {\n        \"preloadTemplate\": \"./resources/preload.html\"\n      }\n    }\n  },\n  \"dependencies\": {\n    \"@theia/ai-anthropic\": \"1.72.0-next.20\",\n    \"@theia/ai-chat\": \"1.72.0-next.20\",\n    \"@theia/ai-chat-ui\": \"1.72.0-next.20\",\n    \"@theia/ai-claude-code\": \"1.72.0-next.20\",\n    \"@theia/ai-code-completion\": \"1.72.0-next.20\",\n    \"@theia/ai-codex\": \"1.72.0-next.20\",\n    \"@theia/ai-copilot\": \"1.72.0-next.20\",\n    \"@theia/ai-core\": \"1.72.0-next.20\",\n    \"@theia/ai-core-ui\": \"1.72.0-next.20\",\n    \"@theia/ai-editor\": \"1.72.0-next.20\",\n    \"@theia/ai-google\": \"1.72.0-next.20\",\n    \"@theia/ai-history\": \"1.72.0-next.20\",\n    \"@theia/ai-huggingface\": \"1.72.0-next.20\",\n    \"@theia/ai-ide\": \"1.72.0-next.20\",\n    \"@theia/ai-llamafile\": \"1.72.0-next.20\",\n    \"@theia/ai-mcp\": \"1.72.0-next.20\",\n    \"@theia/ai-mcp-server\": \"1.72.0-next.20\",\n    \"@theia/ai-mcp-ui\": \"1.72.0-next.20\",\n    \"@theia/ai-ollama\": \"1.72.0-next.20\",\n    \"@theia/ai-openai\": \"1.72.0-next.20\",\n    \"@theia/ai-scanoss\": \"1.72.0-next.20\",\n    \"@theia/ai-terminal\": \"1.72.0-next.20\",\n    \"@theia/ai-vercel-ai\": \"1.72.0-next.20\",\n    \"@theia/bulk-edit\": \"1.72.0-next.20\",\n    \"@theia/callhierarchy\": \"1.72.0-next.20\",\n    \"@theia/collaboration\": \"1.72.0-next.20\",\n    \"@theia/console\": \"1.72.0-next.20\",\n    \"@theia/core\": \"1.72.0-next.20\",\n    \"@theia/debug\": \"1.72.0-next.20\",\n    \"@theia/dev-container\": \"1.72.0-next.20\",\n    \"@theia/editor\": \"1.72.0-next.20\",\n    \"@theia/editor-preview\": \"1.72.0-next.20\",\n    \"@theia/electron\": \"1.72.0-next.20\",\n    \"@theia/external-terminal\": \"1.72.0-next.20\",\n    \"@theia/file-search\": \"1.72.0-next.20\",\n    \"@theia/filesystem\": \"1.72.0-next.20\",\n    \"@theia/getting-started\": \"1.72.0-next.20\",\n    \"@theia/keymaps\": \"1.72.0-next.20\",\n    \"@theia/markers\": \"1.72.0-next.20\",\n    \"@theia/memory-inspector\": \"1.72.0-next.20\",\n    \"@theia/messages\": \"1.72.0-next.20\",\n    \"@theia/metrics\": \"1.72.0-next.20\",\n    \"@theia/mini-browser\": \"1.72.0-next.20\",\n    \"@theia/monaco\": \"1.72.0-next.20\",\n    \"@theia/navigator\": \"1.72.0-next.20\",\n    \"@theia/notebook\": \"1.72.0-next.20\",\n    \"@theia/outline-view\": \"1.72.0-next.20\",\n    \"@theia/output\": \"1.72.0-next.20\",\n    \"@theia/plugin-dev\": \"1.72.0-next.20\",\n    \"@theia/plugin-ext\": \"1.72.0-next.20\",\n    \"@theia/plugin-ext-vscode\": \"1.72.0-next.20\",\n    \"@theia/preferences\": \"1.72.0-next.20\",\n    \"@theia/preview\": \"1.72.0-next.20\",\n    \"@theia/process\": \"1.72.0-next.20\",\n    \"@theia/property-view\": \"1.72.0-next.20\",\n    \"@theia/remote\": \"1.72.0-next.20\",\n    \"@theia/remote-wsl\": \"1.72.0-next.20\",\n    \"@theia/scanoss\": \"1.72.0-next.20\",\n    \"@theia/scm\": \"1.72.0-next.20\",\n    \"@theia/search-in-workspace\": \"1.72.0-next.20\",\n    \"@theia/secondary-window\": \"1.72.0-next.20\",\n    \"@theia/task\": \"1.72.0-next.20\",\n    \"@theia/terminal\": \"1.72.0-next.20\",\n    \"@theia/terminal-manager\": \"1.72.0-next.20\",\n    \"@theia/test\": \"1.72.0-next.20\",\n    \"@theia/timeline\": \"1.72.0-next.20\",\n    \"@theia/toolbar\": \"1.72.0-next.20\",\n    \"@theia/typehierarchy\": \"1.72.0-next.20\",\n    \"@theia/userstorage\": \"1.72.0-next.20\",\n    \"@theia/variable-resolver\": \"1.72.0-next.20\",\n    \"@theia/vsx-registry\": \"1.72.0-next.20\",\n    \"@theia/workspace\": \"1.72.0-next.20\",\n    \"fs-extra\": \"^9.1.0\",\n    \"theia-ide-launcher-ext\": \"1.71.100\",\n    \"theia-ide-product-ext\": \"1.71.100\",\n    \"theia-ide-updater-ext\": \"1.71.100\"\n  },\n  \"devDependencies\": {\n    \"@theia/cli\": \"1.72.0-next.20\",\n    \"@theia/bundle-plugin\": \"1.72.0-next.20\",\n    \"@types/js-yaml\": \"^3.12.10\",\n    \"@types/yargs\": \"17.0.7\",\n    \"@wdio/cli\": \"^6.12.1\",\n    \"@wdio/local-runner\": \"^6.12.1\",\n    \"@wdio/mocha-framework\": \"^6.11.0\",\n    \"@wdio/spec-reporter\": \"^6.11.0\",\n    \"app-builder-lib\": \"26.0.12\",\n    \"chai\": \"^4.5.0\",\n    \"concurrently\": \"^3.6.1\",\n    \"electron\": \"39.8.7\",\n    \"electron-builder\": \"26.0.12\",\n    \"electron-chromedriver\": \"^28.3.3\",\n    \"electron-mocha\": \"^12.3.1\",\n    \"electron-osx-sign\": \"^0.6.0\",\n    \"js-yaml\": \"^3.14.2\",\n    \"mocha\": \"^8.4.0\",\n    \"rimraf\": \"^2.7.1\",\n    \"ts-node\": \"^10.9.2\",\n    \"wdio-chromedriver-service\": \"^6.0.4\",\n    \"webdriverio\": \"^6.12.1\",\n    \"yargs\": \"17.2.1\"\n  },\n  \"scripts\": {\n    \"clean\": \"theia clean && rimraf node_modules\",\n    \"clean:dist\": \"rimraf dist\",\n    \"build\": \"yarn -s rebuild && theia build --app-target=\\\"electron\\\" --mode development\",\n    \"build:prod\": \"yarn -s rebuild && theia build --app-target=\\\"electron\\\"\",\n    \"rebuild\": \"theia rebuild:electron --cacheRoot ../..\",\n    \"watch\": \"concurrently -n compile,build \\\"theiaext watch --preserveWatchOutput\\\" \\\"theia build --watch --mode development\\\"\",\n    \"start\": \"electron scripts/theia-electron-main.js --plugins=local-dir:../../plugins\",\n    \"start:debug\": \"yarn start --log-level=debug\",\n    \"package\": \"yarn clean:dist && yarn rebuild && electron-builder -c.mac.identity=null --publish never\",\n    \"package:prod\": \"yarn deploy\",\n    \"deploy\": \"yarn clean:dist && yarn rebuild && electron-builder -c.mac.identity=null --publish always\",\n    \"package:preview\": \"yarn clean:dist && yarn rebuild && electron-builder -c.mac.identity=null --dir\",\n    \"update:checksum\": \"ts-node scripts/update-checksum.ts\",\n    \"update:blockmap\": \"ts-node scripts/update-blockmap.ts\",\n    \"update:theia\": \"ts-node ../../scripts/update-theia-version.ts\",\n    \"update:next\": \"ts-node ../../scripts/update-theia-version.ts next\",\n    \"sign:directory\": \"ts-node scripts/sign-directory.ts\",\n    \"sign:directory:windows\": \"ts-node scripts/sign-directory-windows.ts\",\n    \"test\": \"mocha --timeout 60000 \\\"./test/*.spec.js\\\"\",\n    \"lint\": \"eslint --ext js,jsx,ts,tsx scripts && eslint --ext js,jsx,ts,tsx test\",\n    \"lint:fix\": \"eslint --ext js,jsx,ts,tsx scripts --fix && eslint --ext js,jsx,ts,tsx test -fix\"\n  }\n}"
  },
  {
    "path": "applications/electron/resources/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Eclipse Theia Blueprint Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "applications/electron/resources/icons/MacLauncherIcons/icon.icon/icon.json",
    "content": "{\n  \"fill\" : {\n    \"automatic-gradient\" : \"extended-srgb:0.00000,0.53333,1.00000,1.00000\"\n  },\n  \"groups\" : [\n    {\n      \"layers\" : [\n        {\n          \"image-name\" : \"icon.png\",\n          \"name\" : \"icon\",\n          \"position\" : {\n            \"scale\" : 1.67,\n            \"translation-in-points\" : [\n              0,\n              0\n            ]\n          }\n        }\n      ],\n      \"name\" : \"Icon\",\n      \"shadow\" : {\n        \"kind\" : \"neutral\",\n        \"opacity\" : 0.5\n      },\n      \"translucency\" : {\n        \"enabled\" : true,\n        \"value\" : 0.5\n      }\n    },\n    {\n      \"blur-material\" : null,\n      \"hidden\" : false,\n      \"layers\" : [\n        {\n          \"image-name\" : \"Theia Light BG.jpg\",\n          \"name\" : \"Theia Light BG\"\n        }\n      ],\n      \"lighting\" : \"combined\",\n      \"name\" : \"Light Mode Content\",\n      \"opacity-specializations\" : [\n        {\n          \"appearance\" : \"dark\",\n          \"value\" : 0\n        }\n      ],\n      \"shadow\" : {\n        \"kind\" : \"neutral\",\n        \"opacity\" : 0.5\n      },\n      \"translucency\" : {\n        \"enabled\" : true,\n        \"value\" : 0.5\n      }\n    },\n    {\n      \"hidden\" : false,\n      \"layers\" : [\n        {\n          \"image-name\" : \"Theia Dark BG.jpg\",\n          \"name\" : \"Theia Dark BG\"\n        }\n      ],\n      \"lighting\" : \"combined\",\n      \"name\" : \"Dark Mode Content\",\n      \"opacity-specializations\" : [\n        {\n          \"value\" : 0\n        },\n        {\n          \"appearance\" : \"dark\",\n          \"value\" : 1\n        }\n      ],\n      \"shadow\" : {\n        \"kind\" : \"neutral\",\n        \"opacity\" : 0.5\n      },\n      \"translucency\" : {\n        \"enabled\" : true,\n        \"value\" : 0.5\n      }\n    }\n  ],\n  \"supported-platforms\" : {\n    \"circles\" : [\n      \"watchOS\"\n    ],\n    \"squares\" : \"shared\"\n  }\n}"
  },
  {
    "path": "applications/electron/resources/preload.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <style>\n        html,\n        body {\n            background-color: black;\n        }\n\n        .theia-preload {\n            position: absolute;\n            top: 0;\n            left: 0;\n            right: 0;\n            bottom: 0;\n            /* Above styles copied from https://github.com/eclipse-theia/theia/blob/5aeef6c0c683b4e91713ab736957e6655b486adc/packages/core/src/browser/style/index.css#L147-L151 */\n            /* Otherwise, there is a flickering when Theia's CSS loads. */\n\n            background-image: none;\n        }\n\n        .theia-preload::after {\n            /* remove default loading animation */\n            content: none;\n        }\n\n        .spinner-container {\n            display: flex;\n            flex-direction: center;\n            align-self: center;\n            justify-content: center;\n            height: 100vh;\n            width: 100vw;\n        }\n\n        .custom-spinner {\n            align-self: center;\n        }\n\n        .custom-spinner svg {\n            width: 16vw;\n            height: 16vh;\n            animation-delay: 0;\n            animation-duration: 2s;\n            animation-iteration-count: infinite;\n            animation-name: theia-ide-spinner;\n            animation-timing-function: ease;\n        }\n\n        @keyframes theia-ide-spinner {\n            0% {\n                filter: invert(49%) sepia(71%) saturate(5980%) hue-rotate(199deg) brightness(103%) contrast(101%);\n                transform: scale(1.0);\n            }\n\n            50% {\n                filter: invert(57%) sepia(52%) saturate(1900%) hue-rotate(160deg) brightness(100%) contrast(102%);\n                transform: scale(0.8);\n            }\n\n            100% {\n                filter: invert(49%) sepia(71%) saturate(5980%) hue-rotate(199deg) brightness(103%) contrast(101%);\n                transform: scale(1.0);\n            }\n        }\n    </style>\n</head>\n\n<body>\n    <div class='spinner-container'>\n        <div class='custom-spinner'>\n            <svg id=\"spinner\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"\n                xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0\" y=\"0\" preserveAspectRatio=\"xMinYMin meet\"\n                viewBox=\"0, 0, 1150, 540.6\">\n                <g id=\"Layer_1\" fill=\"#FFFFFF\">\n                    <path\n                        d=\"M880.199,2.8 C1028.1,2.8 1147.9,122.6 1147.9,270.5 C1147.9,418.3 1028.1,538.2 880.2,538.2 L290.1,538.2 C269,538.2 251.9,521.1 251.9,500 C251.9,478.9 269,461.8 290.1,461.8 L427.6,461.8 C448.6,461.8 465.7,444.7 465.7,423.6 C465.7,402.5 448.6,385.4 427.6,385.4 L396.999,385.4 C375.9,385.4 358.8,368.3 358.8,347.2 C358.8,326.1 375.9,309 397,309 L488.703,309 C509.918,308.941 526.373,291.65 526.9,270.8 C526.9,249.7 509.8,232.6 488.7,232.6 L167.8,232.6 C146.7,232.6 129.6,215.5 129.6,194.4 C129.6,173.3 146.7,156.2 167.8,156.2 L404.604,156.2 C425.818,156.141 442.273,138.85 442.8,118 C442.8,96.9 425.7,79.8 404.6,79.8 L351.2,79.8 C330.1,79.8 313,62.7 313,41.6 C313,20.5 330.1,2.4 351.2,2.4 L880.199,2.8 z M837.4,92 L837.4,92 C755.2,92 688.7,158.6 688.7,240.7 L688.7,300.2 C688.7,382.4 755.2,448.9 837.4,448.9 C919.5,448.9 986.1,382.4 986.1,300.2 L986.1,240.7 C986.1,158.6 919.5,92 837.4,92 L837.4,92 z M888.2,232.6 C908,232.6 924.1,248.7 924.1,268.5 L924.1,273.1 C924.1,292.9 908,309 888.2,309 L776.6,309 C756.8,309 740.7,292.9 740.7,273.1 L740.7,268.5 C740.7,248.7 756.8,232.6 776.6,232.6 L888.2,232.6 z\" />\n                    <path\n                        d=\"M170.1,461.8 C190,461.8 206,477.8 206,497.7 L206,502.3 C206,522.1 190,538.2 170.1,538.2 L38,538.2 C18.2,538.2 2.1,522.1 2.1,502.3 L2.1,497.7 C2.1,477.8 18.2,461.8 38,461.8 L170.1,461.8 z\" />\n                    <path\n                        d=\"M231.3,3.4 C251.1,3.4 267.1,19.5 267.1,39.3 L267.1,44 C267.1,63.8 251.1,79.8 231.3,79.8 L83.8,79.8 C64,79.8 47.9,63.8 47.9,44 L47.9,39.3 C47.9,19.5 64,3.4 83.8,3.4 L231.3,3.4 z\" />\n                    <path\n                        d=\"M277.1,309 C296.9,309 313,325.1 313,344.9 L313,349.5 C313,369.3 296.9,385.4 277.1,385.4 L196.1,385.4 C176.3,385.4 160.2,369.3 160.2,349.5 L160.2,344.9 C160.2,325.1 176.3,309 196.1,309 L277.1,309 z\" />\n                </g>\n            </svg>\n        </div>\n    </div>\n</body>\n\n</html>"
  },
  {
    "path": "applications/electron/scripts/after-pack.js",
    "content": "#!/usr/bin/env node\n\nconst fs = require('fs');\nconst path = require('path');\nconst util = require('util');\nconst child_process = require('child_process');\nconst rimraf = require('rimraf');\nconst sign_util = require('electron-osx-sign/util');\nconst asyncRimraf = util.promisify(rimraf);\n\nconst DELETE_PATHS = [\n    'Contents/Resources/app/node_modules/unzip-stream/aa.zip',\n    'Contents/Resources/app/node_modules/unzip-stream/testData*'\n];\n\nconst signCommand = path.join(__dirname, 'sign.sh');\nconst notarizeCommand = path.join(__dirname, 'notarize.sh');\nconst entitlements = path.resolve(__dirname, '..', 'entitlements.plist');\n\nconst signFile = file => {\n    const stat = fs.lstatSync(file);\n    const mode = stat.isFile() ? stat.mode : undefined;\n\n    console.log(`Signing ${file}...`);\n    child_process.spawnSync(signCommand, [\n        path.basename(file),\n        entitlements\n    ], {\n        cwd: path.dirname(file),\n        maxBuffer: 1024 * 10000,\n        env: process.env,\n        stdio: 'inherit',\n        encoding: 'utf-8'\n    });\n\n    if (mode) {\n        console.log(`Setting attributes of ${file}...`);\n        fs.chmodSync(file, mode);\n    }\n};\n\nexports.default = async function (context) {\n    await afterPackHook(context);\n    const running_ci = process.env.THEIA_IDE_JENKINS_CI === 'true';\n    const releaseDryRun = process.env.THEIA_IDE_JENKINS_RELEASE_DRYRUN === 'true';\n    const branch = process.env.BRANCH_NAME;\n    const running_on_mac = context.packager.platform.name === 'mac';\n    const appPath = path.resolve(context.appOutDir, `${context.packager.appInfo.productFilename}.app`);\n\n    // Remove anything we don't want in the final package\n    for (const deletePath of DELETE_PATHS) {\n        const resolvedPath = path.resolve(appPath, deletePath);\n        console.log(`Deleting ${resolvedPath}...`);\n        await asyncRimraf(resolvedPath);\n    }\n\n    // Only continue for macOS during CI\n    if ((( branch === 'master' || releaseDryRun)  && running_ci && running_on_mac)) {\n        console.log('Detected Theia IDE Release on Mac ' + releaseDryRun ? ' (dry-run)' : ''\n            + ' - proceeding with signing and notarizing');\n    } else {\n        if (running_on_mac) {\n            console.log('Not a release or dry-run requiring signing/notarizing - skipping');\n        }\n        return;\n    }\n\n    // Use app-builder-lib to find all binaries to sign, at this level it will include the final .app\n    let childPaths = await sign_util.walkAsync(context.appOutDir);\n\n    // Sign deepest first\n    // From https://github.com/electron-userland/electron-builder/blob/master/packages/app-builder-lib/electron-osx-sign/sign.js#L120\n    childPaths = childPaths.sort((a, b) => {\n        const aDepth = a.split(path.sep).length;\n        const bDepth = b.split(path.sep).length;\n        return bDepth - aDepth;\n    });\n\n    // Sign binaries\n    childPaths.forEach(file => signFile(file, context.appOutDir));\n\n    // Notarize app\n    child_process.spawnSync(notarizeCommand, [\n        path.basename(appPath),\n        context.packager.appInfo.info._configuration.appId\n    ], {\n        cwd: path.dirname(appPath),\n        maxBuffer: 1024 * 10000,\n        env: process.env,\n        stdio: 'inherit',\n        encoding: 'utf-8'\n    });\n};\n\n// taken and modified from: https://github.com/gergof/electron-builder-sandbox-fix/blob/a2251d7d8f22be807d2142da0cf768c78d4cfb0a/lib/index.js\nconst afterPackHook = async params => {\n    if (params.electronPlatformName !== 'linux') {\n        // this fix is only required on linux\n        return;\n    }\n    const executable = path.join(\n        params.appOutDir,\n        params.packager.executableName\n    );\n\n    const loaderScript = `#!/usr/bin/env bash\nset -u\nSCRIPT_DIR=\"$( cd \"$( dirname \"\\${BASH_SOURCE[0]}\" )\" && pwd )\"\nexec \"$SCRIPT_DIR/${params.packager.executableName}.bin\" \"--no-sandbox\" \"$@\"\n`;\n\n    try {\n        await fs.promises.rename(executable, executable + '.bin');\n        await fs.promises.writeFile(executable, loaderScript);\n        await fs.promises.chmod(executable, 0o755);\n    } catch (e) {\n        throw new Error('Failed to create loader for sandbox fix:\\n' + e);\n    }\n};\n"
  },
  {
    "path": "applications/electron/scripts/appimage-helpers.js",
    "content": "const fs = require('fs');\nconst path = require('path');\n\n/**\n * Reads the plugin copy metadata file and returns its content.\n * @param metadataPath - Path to the metadata file\n * @returns The metadata object or undefined if not found\n */\nfunction readPluginCopyMetadata(metadataPath) {\n    if (!fs.existsSync(metadataPath)) {\n        return undefined;\n    }\n    try {\n        return JSON.parse(fs.readFileSync(metadataPath, 'utf8'));\n    } catch (err) {\n        console.warn('Could not read built-in plugin copy metadata file:', err.message);\n        return undefined;\n    }\n}\n\n/**\n * Writes the plugin copy metadata file with version and timestamp.\n * @param metadataPath - Path to the metadata file\n * @param version - Current version\n */\nfunction writePluginCopyMetadata(metadataPath, version) {\n    const metadata = {\n        version: version,\n        copiedAt: new Date().toISOString()\n    };\n    fs.writeFileSync(metadataPath, JSON.stringify(metadata, undefined, 2));\n}\n\n/**\n * Copies bundled plugins from AppImage to user directory if needed.\n * @param bundledPluginsDir - Path to bundled plugins in AppImage\n * @param userPluginsDir - Path to user built-in plugins directory\n * @param currentVersion - Current Theia IDE version\n * @returns true if the builtins were copied to the user dir, false if there was an error\n */\nfunction copyBundledPlugins(bundledPluginsDir, userPluginsDir, currentVersion) {\n    const metadataFile = path.join(userPluginsDir, '.builtInPlugins-metadata');\n\n    // Ensure the user plugins directory exists\n    if (!fs.existsSync(userPluginsDir)) {\n        fs.mkdirSync(userPluginsDir, { recursive: true });\n    }\n\n    // Check if built-in plugins need to be copied\n    const metadata = readPluginCopyMetadata(metadataFile);\n    let shouldCopy = false;\n\n    if (!metadata) {\n        shouldCopy = true;\n    } else if (metadata.version !== currentVersion) {\n        console.log(`Theia IDE updated from ${metadata.version} to ${currentVersion}. Updating built-in plugins...`);\n        shouldCopy = true;\n    }\n\n    if (!shouldCopy) {\n        console.log('Built-in plugins were already copied.');\n        return true;\n    }\n\n    console.log(`Copying bundled plugins from AppImage to ${userPluginsDir}...`);\n    try {\n        // Clean existing plugins directory to remove old/obsolete plugins\n        fs.rmSync(userPluginsDir, { recursive: true, force: true });\n        fs.mkdirSync(userPluginsDir, { recursive: true });\n\n        const pluginEntries = fs.readdirSync(bundledPluginsDir, { withFileTypes: true });\n        for (const entry of pluginEntries) {\n            const srcPath = path.join(bundledPluginsDir, entry.name);\n            const destPath = path.join(userPluginsDir, entry.name);\n            fs.cpSync(srcPath, destPath, { recursive: true });\n        }\n        writePluginCopyMetadata(metadataFile, currentVersion);\n        console.log(`Bundled plugins copied successfully to ${userPluginsDir}.`);\n    } catch (err) {\n        console.error('Failed to copy bundled plugins:', err.message);\n        return false;\n    }\n    return true;\n}\n\nmodule.exports = {\n    copyBundledPlugins,\n};\n"
  },
  {
    "path": "applications/electron/scripts/generate-app-update-yml.js",
    "content": "#!/usr/bin/env node\n\n/********************************************************************************\n * Copyright (C) 2026 STMicroelectronics and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\n// Generates app-update.yml for the Windows auto-updater.\n//\n// The normal electron-builder flow generates this file during afterPack, but\n// only when the target is \"nsis\" or \"appx\". The Windows CI build splits\n// packaging into two steps:\n//   1. `electron-builder --dir` (target = \"dir\") → app-update.yml is skipped\n//   2. `electron-builder --prepackaged` → afterPack does not run\n//\n// This script bridges that gap by writing the file before step 2.\n\nconst fs = require('fs');\nconst path = require('path');\nconst yaml = require('js-yaml');\n\nconst electronDir = path.resolve(__dirname, '..');\nconst pkg = require(path.join(electronDir, 'package.json'));\nconst builderConfig = yaml.load(fs.readFileSync(path.join(electronDir, 'electron-builder.yml'), 'utf8'));\n\nconst winPublish = builderConfig.win.publish;\nconst version = pkg.version;\n\n// Expand ${version} macro in the URL, matching electron-builder's macro expansion\nconst url = winPublish.url.replace('${version}', version);\n\nconst appUpdateYml = {\n    provider: winPublish.provider,\n    url,\n    ...(winPublish.useMultipleRangeRequest !== undefined && { useMultipleRangeRequest: winPublish.useMultipleRangeRequest }),\n    updaterCacheDirName: `${pkg.name}-updater`\n};\n\nconst outPath = path.join(electronDir, 'dist', 'win-unpacked', 'resources', 'app-update.yml');\nfs.writeFileSync(outPath, yaml.dump(appUpdateYml, { lineWidth: -1 }));\nconsole.log(`Generated ${outPath}`);\nconsole.log(fs.readFileSync(outPath, 'utf8'));\n"
  },
  {
    "path": "applications/electron/scripts/notarize.sh",
    "content": "#!/bin/bash -x\n\nINPUT=$1\nAPP_ID=$2\nNEEDS_UNZIP=false\nUUID_REGEX='\"uuid\"\\s*:\\s*\"([^\"]+)'\nSTATUS_REGEX='\"status\"\\s*:\\s*\"([^\"]+)'\n\n# if folder, zip it\nif [ -d \"${INPUT}\" ]; then\n    NEEDS_UNZIP=true\n    zip -r -q -y unsigned.zip \"${INPUT}\"\n    rm -rf \"${INPUT}\"\n    INPUT=unsigned.zip\nfi\n\n# copy file to storage server\nscp -p \"${INPUT}\" genie.theia@projects-storage.eclipse.org:./\nrm -f \"${INPUT}\"\n\n# name to use on server\nREMOTE_NAME=${INPUT##*/}\n\n# notarize over ssh\nRESPONSE=$(ssh -q genie.theia@projects-storage.eclipse.org curl -X POST -F file=@\"\\\"${REMOTE_NAME}\\\"\" -F \"'options={\\\"primaryBundleId\\\": \\\"${APP_ID}\\\", \\\"staple\\\": true};type=application/json'\" https://cbi.eclipse.org/macos/xcrun/notarize)\n\n# fund uuid and status\n[[ $RESPONSE =~ $UUID_REGEX ]]\nUUID=${BASH_REMATCH[1]}\n[[ $RESPONSE =~ $STATUS_REGEX ]]\nSTATUS=${BASH_REMATCH[1]}\n\n# poll progress\necho \"  Progress: $RESPONSE\"\nwhile [[ $STATUS == 'IN_PROGRESS' ]]; do\n    sleep 120\n    RESPONSE=$(ssh -q genie.theia@projects-storage.eclipse.org curl -s https://cbi.eclipse.org/macos/xcrun/${UUID}/status)\n    [[ $RESPONSE =~ $STATUS_REGEX ]]\n    STATUS=${BASH_REMATCH[1]}\n    echo \"  Progress: $RESPONSE\"\ndone\n\nif [[ $STATUS != 'COMPLETE' ]]; then\n    echo \"Notarization failed: $RESPONSE\"\n    exit 1\nfi\n\n# download stapled result\nssh -q genie.theia@projects-storage.eclipse.org curl -o \"\\\"stapled-${REMOTE_NAME}\\\"\" https://cbi.eclipse.org/macos/xcrun/${UUID}/download\n\n# copy stapled file back from server\nscp -T -p genie.theia@projects-storage.eclipse.org:\"\\\"./stapled-${REMOTE_NAME}\\\"\" \"${INPUT}\"\n\n# ensure storage server is clean\nssh -q genie.theia@projects-storage.eclipse.org rm -f \"\\\"${REMOTE_NAME}\\\"\" \"\\\"stapled-${REMOTE_NAME}\\\"\" entitlements.plist\n\n# if unzip needed\nif [ \"$NEEDS_UNZIP\" = true ]; then\n    unzip -qq \"${INPUT}\"\n\n    if [ $? -ne 0 ]; then\n        # echo contents if unzip failed\n        output=$(cat $INPUT)\n        echo \"$output\"\n        exit 1\n    fi\n\n    rm -f \"${INPUT}\"\nfi\n"
  },
  {
    "path": "applications/electron/scripts/sign-directory-windows.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2026 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport child_process from 'child_process';\nimport fs from 'fs';\nimport path from 'path';\nimport { hideBin } from 'yargs/helpers';\nimport yargs from 'yargs/yargs';\nimport { formatBytes, replaceWithSigned, walkFiles } from './sign-utils';\n\n/**\n * Sign Windows binaries inside an electron-builder `--dir` output (`win-unpacked`) by uploading\n * each `.exe` to the Eclipse Authenticode signing service and writing the signed result back in\n * place. The traversal must mirror the roots that electron-builder's `winPackager.signApp()`\n * would sign: the top level of the unpacked directory, `resources/app.asar.unpacked/` and\n * `swiftshader/`. The filter (`.exe` only) matches electron-builder's default `shouldSignFile`\n * when `win.signExts` is unset.\n */\n\nconst DEFAULT_SIGN_URL = 'https://cbi.eclipse.org/authenticode/sign';\n\nconst argv = yargs(hideBin(process.argv))\n    .option('directory', {\n        alias: 'd',\n        type: 'string',\n        demandOption: true,\n        description: 'The electron-builder `--dir` output directory (e.g. dist/win-unpacked) containing the unpacked Windows app'\n    })\n    .option('url', {\n        alias: 'u',\n        type: 'string',\n        default: DEFAULT_SIGN_URL,\n        description: 'The URL of the Authenticode signing service'\n    })\n    .version(false)\n    .wrap(120)\n    .parseSync();\n\nfunction isExeFile(filePath: string): boolean {\n    return path.extname(filePath).toLowerCase() === '.exe';\n}\n\n/**\n * Collect files to sign by reproducing electron-builder's `winPackager.signApp()` traversal:\n *  1. Top-level files directly under `<dir>/` (no recursion)\n *  2. Recursive walk of `<dir>/resources/app.asar.unpacked/`\n *  3. Recursive walk of `<dir>/swiftshader/` (if present)\n */\nfunction collectFilesToSign(unpackedDir: string): string[] {\n    const topLevel = walkFiles(unpackedDir, isExeFile, { shallow: true });\n    const asarUnpacked = walkFiles(path.join(unpackedDir, 'resources', 'app.asar.unpacked'), isExeFile);\n    const swiftshader = walkFiles(path.join(unpackedDir, 'swiftshader'), isExeFile);\n    return [...topLevel, ...asarUnpacked, ...swiftshader];\n}\n\nfunction signFile(file: string, url: string): void {\n    const signedPath = `${file}.signed`;\n    // Remove any stale signed file from a previous run\n    if (fs.existsSync(signedPath)) {\n        fs.unlinkSync(signedPath);\n    }\n\n    const before = fs.statSync(file);\n    console.log(`Signing ${file} (${formatBytes(before.size)})...`);\n\n    const started = Date.now();\n    const result = child_process.spawnSync(\n        'curl',\n        ['-sSf', '-o', signedPath, '-F', `file=@${file}`, url],\n        { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf-8' }\n    );\n    const durationMs = Date.now() - started;\n\n    if (result.error) {\n        throw new Error(`Failed to invoke curl for ${file}: ${result.error.message}`);\n    }\n    if (result.status !== 0) {\n        // Dump whatever curl produced to help diagnose. If curl wrote a non-binary error payload to\n        // `signedPath` (e.g. an HTML/JSON error page from the signing service), log that too.\n        if (result.stdout) {\n            console.error(`curl stdout:\\n${result.stdout}`);\n        }\n        if (result.stderr) {\n            console.error(`curl stderr:\\n${result.stderr}`);\n        }\n        if (fs.existsSync(signedPath)) {\n            try {\n                const body = fs.readFileSync(signedPath, 'utf-8');\n                console.error(`Response body written to ${signedPath}:\\n${body}`);\n            } catch {\n                // Ignore read errors on binary/garbage content\n            }\n            try {\n                fs.unlinkSync(signedPath);\n            } catch {\n                // Best-effort cleanup\n            }\n        }\n        throw new Error(`Signing failed for ${file}: curl exited with status ${result.status}`);\n    }\n\n    if (!fs.existsSync(signedPath)) {\n        throw new Error(`Signing failed for ${file}: no output file produced at ${signedPath}`);\n    }\n\n    const signedSize = fs.statSync(signedPath).size;\n    if (signedSize === 0) {\n        fs.unlinkSync(signedPath);\n        throw new Error(`Signing failed for ${file}: signed output is empty`);\n    }\n\n    replaceWithSigned(file, signedPath);\n    console.log(`  -> signed in ${durationMs} ms (${formatBytes(signedSize)})`);\n}\n\nfunction main(): void {\n    const directory = path.resolve(argv.directory);\n    const url = argv.url;\n\n    if (!fs.existsSync(directory) || !fs.statSync(directory).isDirectory()) {\n        console.error(`Directory does not exist or is not a directory: ${directory}`);\n        process.exit(1);\n    }\n\n    console.log(`sign-directory-windows: directory=${directory}; url=${url}`);\n\n    const files = collectFilesToSign(directory);\n    if (files.length === 0) {\n        // Having zero files usually means the app layout changed and our walk roots no longer\n        // match what electron-builder would sign. Fail so this is caught in CI rather than\n        // silently shipping an unsigned binary.\n        console.error(\n            `No .exe files found under ${directory} in any of the expected roots (top level, ` +\n            'resources/app.asar.unpacked, swiftshader). This likely indicates a change in the ' +\n            'electron-builder output layout that sign-directory-windows has not been updated for.'\n        );\n        process.exit(1);\n    }\n\n    console.log(`Found ${files.length} file(s) to sign:`);\n    for (const file of files) {\n        console.log(`  - ${path.relative(directory, file)}`);\n    }\n\n    const overallStarted = Date.now();\n    for (const file of files) {\n        signFile(file, url);\n    }\n    const overallDuration = Date.now() - overallStarted;\n\n    console.log(`Signed ${files.length} file(s) in ${overallDuration} ms.`);\n}\n\nmain();\n"
  },
  {
    "path": "applications/electron/scripts/sign-directory.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2025 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { hideBin } from 'yargs/helpers';\nimport yargs from 'yargs/yargs';\nimport path from 'path';\nimport fs from 'fs';\nimport child_process from 'child_process';\nimport { walkFiles } from './sign-utils';\n\nconst signCommand = path.join(__dirname, 'sign.sh');\nconst notarizeCommand = path.join(__dirname, 'notarize.sh');\nconst entitlements = path.resolve(__dirname, '..', 'entitlements.plist');\n\n// File extensions and patterns that need code signing on macOS\nconst BINARY_EXTENSIONS = ['.dylib', '.so', '.node', '.framework'];\nconst BINARY_PATTERNS = [\n    /^MacOS\\//, // Executable files in MacOS directory\n    /^Contents\\/MacOS\\//, // Executable files in Contents/MacOS directory\n];\nconst EXECUTABLE_NAMES = [\n    'node', 'electron', 'rg', 'macos-trash', 'chrome-sandbox'\n];\n\n// Function to check if a file is likely a binary that needs signing\nfunction isBinaryFile(filePath: string): boolean {\n    const extension = path.extname(filePath);\n    const fileName = path.basename(filePath);\n    const relativePath = filePath.replace(/^.*?\\.app\\//, ''); // Get path relative to .app bundle\n\n    // Check by extension\n    if (BINARY_EXTENSIONS.includes(extension)) {\n        return true;\n    }\n\n    // Check by executable name\n    if (EXECUTABLE_NAMES.includes(fileName)) {\n        return true;\n    }\n\n    // Check by pattern\n    for (const pattern of BINARY_PATTERNS) {\n        if (pattern.test(relativePath)) {\n            return true;\n        }\n    }\n\n    // Check if file is executable (Unix-only check)\n    try {\n        const stat = fs.statSync(filePath);\n        if ((stat.mode & 0o111) !== 0) { // Check if execute bit is set\n            // Further verify it's a binary with 'file' command if available\n            try {\n                const fileType = child_process.execSync(`file \"${filePath}\"`).toString();\n                return fileType.includes('Mach-O') ||\n                       fileType.includes('executable') ||\n                       fileType.includes('shared library') ||\n                       fileType.includes('dynamically linked');\n            } catch (e) {\n                // If 'file' command fails, fall back to assuming it's a binary if it has execute permission\n                return true;\n            }\n        }\n    } catch (e) {\n        // If stat fails, skip this check\n    }\n\n    return false;\n}\n\n// Function to recursively find binaries in a directory\nfunction findBinariesToSign(dirPath: string): string[] {\n    const result = walkFiles(dirPath, isBinaryFile, { skipDirs: ['node_modules', '.git'] });\n\n    // Sort by path depth (deepest first) to ensure nested binaries are signed first\n    return result.sort((a, b) => {\n        const aDepth = a.split(path.sep).length;\n        const bDepth = b.split(path.sep).length;\n        return bDepth - aDepth;\n    });\n}\n\nconst signFile = (file: string) => {\n    const stat = fs.lstatSync(file);\n    const mode = stat.isFile() ? stat.mode : undefined;\n\n    // Get SHA hash of file before signing - only for actual files, not directories\n    let shaBeforeSigning: string | undefined;\n    if (stat.isFile()) {\n        shaBeforeSigning = child_process.execSync(`shasum -a 256 \"${file}\"`).toString().trim();\n    }\n\n    console.log(`Signing ${file}...`);\n    child_process.spawnSync(signCommand, [\n        path.basename(file),\n        entitlements\n    ], {\n        cwd: path.dirname(file),\n        maxBuffer: 1024 * 10000,\n        env: process.env,\n        stdio: 'inherit',\n        encoding: 'utf-8'\n    });\n\n    // Get SHA hash of file after signing - only for actual files, not directories\n    if (stat.isFile()) {\n        const shaAfterSigning = child_process.execSync(`shasum -a 256 \"${file}\"`).toString().trim();\n        // Log a warning if the SHA hash hasn't changed after signing\n        if (shaBeforeSigning === shaAfterSigning) {\n            console.warn(`WARNING: SHA hash did not change after signing for ${file}. This might indicate the file was not properly signed.`);\n        }\n    }\n\n    if (mode) {\n        console.log(`Setting attributes of ${file}...`);\n        fs.chmodSync(file, mode);\n    }\n};\n\nconst argv = yargs(hideBin(process.argv))\n    .option('directory', { alias: 'd', type: 'string', default: 'dist', description: 'The directory which contains the application to be signed' })\n    .version(false)\n    .wrap(120)\n    .parseSync();\n\nexecute();\n\nasync function execute(): Promise<void> {\n    console.log(`signCommand: ${signCommand}; notarizeCommand: ${notarizeCommand}; entitlements: ${entitlements}; directory: ${argv.directory}`);\n\n    // First sign all individual binaries inside the app bundle\n    const binariesToSign = findBinariesToSign(argv.directory);\n\n    for (const binaryPath of binariesToSign) {\n        signFile(binaryPath);\n    }\n\n    // Then sign the main app bundle\n    console.log('Signing main application bundle...');\n    signFile(argv.directory);\n\n    // Notarize app\n    console.log('Notarizing application...');\n    child_process.spawnSync(notarizeCommand, [\n        path.basename(argv.directory),\n        'eclipse.theia'\n    ], {\n        cwd: path.dirname(argv.directory),\n        maxBuffer: 1024 * 10000,\n        env: process.env,\n        stdio: 'inherit',\n        encoding: 'utf-8'\n    });\n}\n"
  },
  {
    "path": "applications/electron/scripts/sign-utils.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2026 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport fs from 'fs';\nimport path from 'path';\n\nexport interface WalkOptions {\n    /** Directory names to skip entirely while recursing (e.g. `['node_modules', '.git']`). */\n    skipDirs?: string[];\n    /** If true, do not recurse into subdirectories - only list direct children. Default: false. */\n    shallow?: boolean;\n}\n\n/**\n * Recursively walks `root` and returns all file paths for which `filter` returns `true`.\n * If `root` does not exist, returns an empty array.\n */\nexport function walkFiles(root: string, filter: (file: string) => boolean, opts: WalkOptions = {}): string[] {\n    const result: string[] = [];\n    if (!fs.existsSync(root)) {\n        return result;\n    }\n    const skipDirs = new Set(opts.skipDirs ?? []);\n\n    const visit = (currentPath: string): void => {\n        const entries = fs.readdirSync(currentPath, { withFileTypes: true });\n        for (const entry of entries) {\n            const fullPath = path.join(currentPath, entry.name);\n            if (entry.isDirectory()) {\n                if (skipDirs.has(entry.name)) {\n                    continue;\n                }\n                if (opts.shallow) {\n                    continue;\n                }\n                visit(fullPath);\n            } else if (entry.isFile() && filter(fullPath)) {\n                result.push(fullPath);\n            }\n        }\n    };\n\n    visit(root);\n    return result;\n    }\n\n/**\n * Replaces `originalPath` with `signedPath` on disk, preserving the file mode of the original.\n * On POSIX `renameSync` atomically replaces the destination, so no separate unlink is needed.\n */\nexport function replaceWithSigned(originalPath: string, signedPath: string): void {\n    const originalMode = fs.statSync(originalPath).mode;\n    fs.renameSync(signedPath, originalPath);\n    fs.chmodSync(originalPath, originalMode);\n}\n\n/** Format a byte count as a human-readable string (e.g. `12.3 MB`). */\nexport function formatBytes(bytes: number): string {\n    if (!Number.isFinite(bytes) || bytes < 0) {\n        return `${bytes}`;\n    }\n    const units = ['B', 'KB', 'MB', 'GB', 'TB'];\n    let value = bytes;\n    let unitIndex = 0;\n    while (value >= 1024 && unitIndex < units.length - 1) {\n        value /= 1024;\n        unitIndex++;\n    }\n    return `${value.toFixed(unitIndex === 0 ? 0 : 1)} ${units[unitIndex]}`;\n}\n"
  },
  {
    "path": "applications/electron/scripts/sign.sh",
    "content": "#!/bin/bash -x\n\n# Enable debug output\nset -x\n\nINPUT=$1\nENTITLEMENTS=$2\nNEEDS_UNZIP=false\n\necho \"=== DEBUG: Starting signing process for $INPUT ===\"\n\n# if folder, zip it\nif [ -d \"${INPUT}\" ]; then\n    echo \"=== DEBUG: Input is a directory, zipping it ===\"\n    NEEDS_UNZIP=true\n    zip -r -q -y unsigned.zip \"${INPUT}\"\n    rm -rf \"${INPUT}\"\n    INPUT=unsigned.zip\nfi\n\n# copy file to storage server\necho \"=== DEBUG: Copying $INPUT to storage server ===\"\nscp -p \"${INPUT}\" genie.theia@projects-storage.eclipse.org:./\nif [ $? -eq 0 ]; then\n    echo \"=== DEBUG: Successfully copied $INPUT to storage server ===\"\nelse\n    echo \"=== ERROR: Failed to copy $INPUT to storage server ===\"\n    exit 1\nfi\nrm -f \"${INPUT}\"\n\n# copy entitlements to storage server\necho \"=== DEBUG: Copying entitlements file to storage server ===\"\nscp -p \"${ENTITLEMENTS}\" genie.theia@projects-storage.eclipse.org:./entitlements.plist\nif [ $? -eq 0 ]; then\n    echo \"=== DEBUG: Successfully copied entitlements to storage server ===\"\nelse\n    echo \"=== ERROR: Failed to copy entitlements to storage server ===\"\n    exit 1\nfi\n\n# name to use on server\nREMOTE_NAME=${INPUT##*/}\n\n# sign over ssh\n# https://wiki.eclipse.org/IT_Infrastructure_Doc#Web_service\nssh -q genie.theia@projects-storage.eclipse.org curl -f -o \"\\\"signed-${REMOTE_NAME}\\\"\" -F file=@\"\\\"${REMOTE_NAME}\\\"\" -F entitlements=@entitlements.plist https://cbi.eclipse.org/macos/codesign/sign\nif [ $? -eq 0 ]; then\n    echo \"=== DEBUG: Remote signing completed successfully ===\"\nelse\n    echo \"=== ERROR: Remote signing failed ===\"\n    # Try to get error information\n    ssh -q genie.theia@projects-storage.eclipse.org \"cat \\\"signed-${REMOTE_NAME}\\\" || echo 'No output file found'\"\n    exit 1\nfi\n\n# copy signed file back from server\necho \"=== DEBUG: Copying signed file back from storage server ===\"\nscp -T -p genie.theia@projects-storage.eclipse.org:\"\\\"./signed-${REMOTE_NAME}\\\"\" \"${INPUT}\"\nif [ $? -eq 0 ]; then\n    echo \"=== DEBUG: Successfully retrieved signed file ===\"\nelse\n    echo \"=== ERROR: Failed to retrieve signed file ===\"\n    exit 1\nfi\n\n# Check if the file was actually signed\necho \"=== DEBUG: Verifying if file was signed properly ===\"\nif [ -f \"${INPUT}\" ]; then\n    # Get file size to verify it's not empty\n    FILE_SIZE=$(stat -f%z \"${INPUT}\" 2>/dev/null || stat -c%s \"${INPUT}\" 2>/dev/null)\n    echo \"=== DEBUG: Signed file size: $FILE_SIZE bytes ===\"\n    \n    # On macOS, we can verify code signature\n    if [[ \"$OSTYPE\" == \"darwin\"* ]]; then\n        echo \"=== DEBUG: Checking code signature with codesign -vv ===\"\n        codesign -vv \"${INPUT}\" || echo \"=== WARNING: codesign verification failed ===\"\n    fi\nelse\n    echo \"=== ERROR: Signed file not found ===\"\n    exit 1\nfi\n\n# ensure storage server is clean\necho \"=== DEBUG: Cleaning up remote files ===\"\nssh -q genie.theia@projects-storage.eclipse.org rm -f \"\\\"${REMOTE_NAME}\\\"\" \"\\\"signed-${REMOTE_NAME}\\\"\" entitlements.plist\necho \"=== DEBUG: Remote cleanup completed ===\"\n\n# if unzip needed\nif [ \"$NEEDS_UNZIP\" = true ]; then\n    echo \"=== DEBUG: Unzipping signed archive ===\"\n    unzip -qq \"${INPUT}\"\n\n    if [ $? -ne 0 ]; then\n        # echo contents if unzip failed\n        echo \"=== ERROR: Unzip failed, showing file contents ===\"\n        output=$(cat $INPUT)\n        echo \"$output\"\n        exit 1\n    fi\n\n    echo \"=== DEBUG: Unzip successful, removing zip file ===\"\n    rm -f \"${INPUT}\"\n\n    # Perform deep codesign check on the directory if running on macOS\n    if [[ \"$OSTYPE\" == \"darwin\"* ]]; then\n        echo \"=== DEBUG: Performing deep codesign verification on directory ===\"\n        # Check if spctl is available (macOS security assessment tool)\n        if command -v spctl &> /dev/null; then\n            # Check if the directory is an app bundle\n            if [[ -d \"$1\" && \"$1\" == *.app ]]; then\n                echo \"=== DEBUG: Verifying app bundle with spctl --assess --verbose ===\"\n                spctl --assess --verbose \"$1\" || echo \"=== WARNING: App bundle verification failed, may not pass notarization ===\"\n            fi\n        fi\n        \n        # Find all binary files and check their signatures\n        echo \"=== DEBUG: Checking individual binary signatures in $1 ===\"\n        find \"$1\" -type f -exec file {} \\; | grep -E \"Mach-O|dylib\" | cut -d: -f1 | while read binary; do\n            echo \"Checking signature for $binary\"\n            codesign --verify --deep --strict --verbose=2 \"$binary\" || echo \"=== WARNING: Binary $binary has signature issues, may not pass notarization ===\"\n            \n            # Check for hardened runtime\n            codesign -d --verbose=4 \"$binary\" 2>&1 | grep -q 'Runtime Version=10.0.0' || echo \"=== WARNING: Binary $binary may not have hardened runtime enabled ===\"\n        done\n    fi\nfi\n\necho \"=== DEBUG: Signing process completed for $1 ===\""
  },
  {
    "path": "applications/electron/scripts/theia-electron-main.js",
    "content": "const path = require('path');\nconst fs = require('fs');\nconst os = require('os');\nconst { copyBundledPlugins } = require('./appimage-helpers');\n\n// Update to override the supported VS Code API version.\n// process.env.VSCODE_API_VERSION = '1.50.0'\n\n// Detect if running as AppImage\nconst isAppImage = !!process.env.APPIMAGE;\n\n// When packaged with asar, __dirname is inside app.asar (e.g., .../app.asar/scripts)\n// but plugins are in extraResources at .../app/plugins (outside the asar)\nconst isInsideAsar = __dirname.includes('.asar');\nconst bundledPluginsDir = isInsideAsar\n    ? path.join(process.resourcesPath, 'app', 'plugins')\n    : path.resolve(__dirname, '../', 'plugins');\n\nif (isAppImage) {\n    // When running as AppImage, use a user-writable directory for the built-in plugins\n    // The AppImage mount point (/tmp/.mount_*) is read-only\n    const configDir = process.env.THEIA_CONFIG_DIR || path.join(os.homedir(), '.theia-ide');\n    const userPluginsDir = path.join(configDir, 'builtInPlugins');\n    const packageJsonPath = path.resolve(__dirname, '../', 'package.json');\n    const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));\n    const currentVersion = packageJson.version;\n\n    // Copy bundled plugins to user directory if needed (first run or version update)\n    const useUserDir = copyBundledPlugins(bundledPluginsDir, userPluginsDir, currentVersion);\n    // If copying fails, fall back to the read-only bundled directory (will be improved in follow up of GH-630)\n    process.env.THEIA_DEFAULT_PLUGINS = `local-dir:${useUserDir ? userPluginsDir : bundledPluginsDir}`;\n\n} else {\n    // Use a set of builtin plugins in our application.\n    process.env.THEIA_DEFAULT_PLUGINS = `local-dir:${bundledPluginsDir}`;\n}\n\n// Handover to the auto-generated electron application handler.\nrequire('../lib/backend/electron-main.js');\n"
  },
  {
    "path": "applications/electron/scripts/update-blockmap.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2023 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { hideBin } from 'yargs/helpers';\nimport yargs from 'yargs/yargs';\nimport { executeAppBuilderAsJson } from 'app-builder-lib/out/util/appBuilder';\n// eslint-disable-next-line import/no-extraneous-dependencies\nimport { BlockMapDataHolder } from 'builder-util-runtime';\nimport { rmSync } from 'fs';\nimport * as path from 'path';\n\nconst BLOCK_MAP_FILE_SUFFIX = '.blockmap';\n\nconst argv = yargs(hideBin(process.argv))\n    .option('executable', { alias: 'e', type: 'string', default: 'TheiaIDE.exe', description: 'The executable for which the blockmap needs to be updated' })\n    .version(false)\n    .wrap(120)\n    .parseSync();\n\nexecute();\n\nasync function execute(): Promise<void> {\n    const executable = argv.executable;\n    const executablePath = path.resolve(\n        __dirname,\n        '../dist/',\n        executable\n    );\n    const blockMapFile = `${executablePath}${BLOCK_MAP_FILE_SUFFIX}`;\n\n    console.log(`Exe: ${executablePath}; Blockmap: ${blockMapFile}`);\n\n    rmSync(blockMapFile, {\n        force: true,\n    });\n    await executeAppBuilderAsJson<BlockMapDataHolder>(['blockmap', '--input', executablePath, '--output', blockMapFile]);\n};\n"
  },
  {
    "path": "applications/electron/scripts/update-checksum.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2021 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\nimport * as crypto from 'crypto';\nimport * as fs from 'fs';\nimport * as jsyaml from 'js-yaml';\nimport * as path from 'path';\nimport { hideBin } from 'yargs/helpers';\nimport yargs from 'yargs/yargs';\n\nconst argv = yargs(hideBin(process.argv))\n    .option('executable', { alias: 'e', type: 'string', default: 'TheiaIDE.AppImage', description: 'The executable for which the checksum needs to be updated' })\n    .option('yaml', { alias: 'y', type: 'string', default: 'latest-linux.yml', description: 'The yaml file where the checksum needs to be updated' })\n    .option('platform', { alias: 'p', type: 'string', default: 'linux', description: 'The OS platform' })\n    .option('updatepaths', { alias: 'u', type: 'boolean', default: true, description: 'Whether to update the paths from absolute to relative' })\n    .option('fileextension', { alias: 'f', type: 'string', default: '.AppImage', description: 'Only paths/urls with this extension will be updated' })\n    .version(false)\n    .wrap(120)\n    .parseSync();\n\nexecute();\n\nasync function execute(): Promise<void> {\n    const executable = argv.executable;\n    const yaml = argv.yaml;\n    const platform = argv.platform;\n    const updatePaths = argv.updatepaths;\n    const fileExtension = argv.fileextension;\n\n    const executablePath = path.resolve(\n        __dirname,\n        '../dist/',\n        executable\n    );\n\n    const yamlPath = path.resolve(\n        __dirname,\n        '../dist/',\n        yaml\n    );\n\n    console.log(`Exe: ${executablePath}; Yaml: ${yamlPath}; Platform: ${platform}; Update Paths: ${updatePaths}; File Extension: ${fileExtension}`);\n\n    const hash = await hashFile(executablePath, 'sha512', 'base64', {});\n    const size = fs.statSync(executablePath).size;\n\n    const yamlContents: string = fs.readFileSync(yamlPath, { encoding: 'utf8' });\n    console.log(`Initial Yaml Contents: ${yamlContents}`);\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    const latestYaml: any = jsyaml.safeLoad(yamlContents);\n\n    if (latestYaml.path.endsWith(fileExtension)) {\n        latestYaml.sha512 = hash;\n        if (updatePaths) {\n            latestYaml.path = updatedPath(latestYaml.path, latestYaml.version, platform);\n        }\n    }\n\n    for (const file of latestYaml.files) {\n        if (file.url.endsWith(fileExtension)) {\n            file.sha512 = hash;\n            file.size = size;\n            if (updatePaths) {\n                file.url = updatedPath(file.url, latestYaml.version, platform);\n            }\n        }\n    }\n\n    // line width -1 to avoid adding >- on long strings like a hash\n    const newYamlContents = jsyaml.dump(latestYaml, { lineWidth: -1 });\n    console.log(`New Yaml Contents: ${newYamlContents}`);\n    fs.writeFileSync(yamlPath, newYamlContents);\n}\n\nfunction hashFile(file: fs.PathLike, algorithm = 'sha512', encoding: BufferEncoding = 'base64', options: string | {\n    flags?: string;\n    encoding?: BufferEncoding;\n    fd?: number;\n    mode?: number;\n    autoClose?: boolean;\n    emitClose?: boolean;\n    start?: number;\n    end?: number;\n    highWaterMark?: number;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\n}): Promise<any> {\n    return new Promise((resolve, reject) => {\n        const hash = crypto.createHash(algorithm);\n        hash.on('error', reject).setEncoding(encoding);\n        fs.createReadStream(\n            file,\n            Object.assign({}, options, {\n                highWaterMark: 1024 * 1024,\n            })\n        )\n            .on('error', reject)\n            .on('end', () => {\n                hash.end();\n                resolve(hash.read());\n            })\n            .pipe(\n                hash,\n                {\n                    end: false,\n                }\n            );\n    });\n}\n\nfunction updatedPath(toUpdate: string, version: string, platform: string): string {\n    const extensionIndex = toUpdate.lastIndexOf('.');\n    return '../../' + version + '/' + platform + '/' + toUpdate.substring(0, extensionIndex) + '-' + version + toUpdate.substring(extensionIndex);\n}\n"
  },
  {
    "path": "applications/electron/test/app.spec.js",
    "content": "const os = require('os');\nconst path = require('path');\nconst fs = require('fs');\nconst { execSync } = require('child_process');\nconst { remote } = require('webdriverio');\nconst { expect } = require('chai');\n\nconst THEIA_LOAD_TIMEOUT = 15000; // 15 seconds\n\n// Set environment variable to disable splash screen (works with asar packaging)\nprocess.env.THEIA_NO_SPLASH = '1';\n\n// Resolve the application directory from cwd so this spec can be shared across products\nconst appDir = process.cwd();\n\n// Directory for saving screenshots on failure or for debugging\nconst screenshotDir = path.join(appDir, 'test-screenshots');\nif (!fs.existsSync(screenshotDir)) {\n  fs.mkdirSync(screenshotDir, { recursive: true });\n}\n\nasync function saveScreenshot(browser, name) {\n  try {\n    const filePath = path.join(screenshotDir, `${name}.png`);\n    await browser.saveScreenshot(filePath);\n    console.log(`Screenshot saved: ${filePath}`);\n  } catch (err) {\n    console.error(`Failed to save screenshot \"${name}\":`, err.message);\n  }\n}\nconst builderConfig = fs.readFileSync(path.join(appDir, 'electron-builder.yml'), 'utf8');\nconst productName = builderConfig.match(/^productName:\\s*(.+)$/m)[1].trim();\nconst packageName = require(path.join(appDir, 'package.json')).name;\n\nfunction isMacArm() {\n  if (os.platform() !== 'darwin') {\n    return false;\n  }\n  try {\n    // Check the architecture using uname -m\n    const arch = execSync('uname -m').toString().trim();\n    return arch === 'arm64';\n  } catch (error) {\n    // Fall back to node's arch property if uname fails\n    return os.arch() === 'arm64';\n  }\n}\n\nfunction getBinaryPath() {\n  const distFolder = path.join(appDir, 'dist');\n  switch (os.platform()) {\n    case 'linux':\n      return path.join(distFolder, 'linux-unpacked', packageName);\n    case 'win32':\n      return path.join(distFolder, 'win-unpacked', `${productName}.exe`);\n    case 'darwin':\n      const macFolder = isMacArm() ? 'mac-arm64' : 'mac';\n      const binaryPath = path.join(\n        distFolder, macFolder, `${productName}.app`, 'Contents', 'MacOS', productName\n      );\n      console.log(`Using binary path for Mac ${isMacArm() ? 'ARM64' : 'Intel'}: ${binaryPath}`);\n      return binaryPath;\n    default:\n      return undefined;\n  }\n};\n\n// Utility for keyboard shortcuts that execute commands where\n// the key combination is the same on all platforms *except that*\n// the Command key is used instead of Control on MacOS. Note that\n// sometimes MacOS also uses Control. This is not handled, here\nfunction macSafeKeyCombo(keys) {\n  if (os.platform() === 'darwin' && keys.includes('Control')) {\n    // Puppeteer calls the Command key \"Meta\"\n    return keys.map(k => k === 'Control' ? 'Meta' : k);\n  }\n  return keys;\n};\n\ndescribe('Theia App', function () {\n  // In mocha, 'this' is a common context between sibling beforeEach, afterEach, it, etc methods within the same describe.\n  // Each describe has its own context.\n  beforeEach(async function () {\n\n    const binary = getBinaryPath();\n    if (!binary) {\n      throw new Error('Tests are not supported for this platform.');\n    }\n\n    // Start app and store connection in context (this)\n    this.browser = await remote({\n      // Change to info to get detailed events of webdriverio\n      logLevel: 'info',\n      capabilities: {\n        browserName: 'chrome',\n        'goog:chromeOptions': {\n          // Path to built and packaged theia\n          binary: binary,\n          // Hand in workspace to load as runtime parameter\n          args: [path.join(appDir, 'test', 'workspace')],\n        },\n      },\n    });\n\n    const appShell = await this.browser.$('#theia-app-shell');\n\n    // mocha waits for returned promise to resolve\n    // Theia is loaded once the app shell is present\n    await appShell.waitForExist({\n      timeout: THEIA_LOAD_TIMEOUT,\n      timeoutMsg: 'Theia took too long to load.',\n    });\n\n    // If workspace trust dialog appears, trust the workspace\n    const dialog = await this.browser.$('.dialogOverlay.workspace-trust-dialog');\n    const dialogAppeared = await dialog.waitForExist({ timeout: 5000 }).catch(() => false);\n\n    if (dialogAppeared) {\n      // Click the main action button to trust the workspace\n      const trustButton = await this.browser.$('.dialogOverlay.workspace-trust-dialog .dialogControl button.theia-button.main');\n      const buttonClickable = await trustButton.waitForClickable({ timeout: 2000 }).catch(() => false);\n      if (buttonClickable) {\n        await trustButton.click();\n        // Wait for dialog to close\n        await dialog.waitForExist({ timeout: 2000, reverse: true }).catch(() => { });\n      }\n    }\n  });\n\n  afterEach(async function () {\n    // Save screenshot on test failure for debugging CI issues\n    if (this.currentTest.state === 'failed') {\n      const testName = this.currentTest.title.replace(/\\s+/g, '-').toLowerCase();\n      await saveScreenshot(this.browser, `FAILED-${testName}`);\n    }\n\n    const CLOSE_TIMEOUT = 10000; // 10 seconds\n    try {\n      await Promise.race([\n        this.browser.closeWindow(),\n        new Promise(resolve => setTimeout(resolve, CLOSE_TIMEOUT))\n      ]);\n    } catch (err) {\n      // Workaround: Puppeteer cannot properly connect to electron and throws an error.\n      // However, the window is closed and that's all we want here.\n      if (`${err}`.includes('Protocol error (Target.createTarget)')) {\n        return;\n      }\n      // Rethrow for unexpected errors to fail test.\n      throw err;\n    }\n  });\n\n  it('Correct window title', async function () {\n    // Wait a bit to make sure workspace is set and title got updated\n    await new Promise(r => setTimeout(r, 2000));\n    const windowTitle = await this.browser.getTitle();\n    expect(windowTitle).to.include('workspace');\n  });\n\n  it('Builtin extensions', async function () {\n    // Wait a bit to make sure key handlers are registered.\n    await new Promise(r => setTimeout(r, 5000));\n\n    // Open extensions view\n    await this.browser.keys(macSafeKeyCombo(['Control', 'Shift', 'x']));\n    const builtinContainer = await this.browser.$(\n      '#vsx-extensions-view-container--vsx-extensions\\\\:builtin'\n    );\n\n    // Expand builtin extensions\n    const builtinHeader = await builtinContainer.$('.theia-header.header');\n    await builtinHeader.moveTo({ xOffset: 1, yOffset: 1 });\n    await builtinHeader.waitForDisplayed();\n    await builtinHeader.waitForClickable();\n    await builtinHeader.click();\n\n    // Wait for expansion to finish (plugins may take time to scan, especially with asar packaging)\n    const builtin = await this.browser.$(\n      '#vsx-extensions\\\\:builtin .theia-TreeContainer'\n    );\n    await builtin.waitForExist({ timeout: 10000 });\n\n    // Get names of all builtin extensions\n    const extensions = await builtin.$$('.theia-vsx-extension .name');\n    const extensionNames = await Promise.all(\n      extensions.map(e => e.getText())\n    );\n\n    // Exemplary check a few extensions\n    expect(extensionNames).to.include('Debugger for Java');\n    expect(extensionNames).to.include('TypeScript and JavaScript Language Features (built-in)');\n  });\n\n  it('Search in workspace', async function () {\n    // Wait a bit to make sure key handlers are registered\n    await new Promise(r => setTimeout(r, 5000));\n\n    // Open search view (Ctrl+Shift+F)\n    await this.browser.keys(macSafeKeyCombo(['Control', 'Shift', 'f']));\n\n    // Wait for search input to appear\n    const searchInput = await this.browser.$('#search-input-field');\n    await searchInput.waitForExist({ timeout: 5000 });\n    await searchInput.waitForDisplayed();\n\n    // Search for text that exists in the test workspace README.md\n    await searchInput.setValue('Test Workspace');\n\n    // Wait for search results to appear\n    const searchResults = await this.browser.$('.t-siw-search-container .resultLine');\n    await searchResults.waitForExist({ timeout: 10000, timeoutMsg: 'Search results did not appear. Ripgrep may not be working correctly with asar packaging.' });\n\n    // Verify we got results\n    const resultsText = await searchResults.getText();\n    expect(resultsText).to.include('Test Workspace');\n  });\n\n  it('Quick file open', async function () {\n    // Wait a bit to make sure key handlers are registered\n    await new Promise(r => setTimeout(r, 5000));\n\n    // Open quick file picker (Ctrl+P)\n    await this.browser.keys(macSafeKeyCombo(['Control', 'p']));\n\n    // Wait for quick input to appear\n    const quickInput = await this.browser.$('.quick-input-widget');\n    await quickInput.waitForExist({ timeout: 5000 });\n    await quickInput.waitForDisplayed();\n\n    // Type filename to search for\n    const inputBox = await this.browser.$('.quick-input-box input');\n    await inputBox.waitForExist({ timeout: 5000 });\n    await inputBox.setValue('README');\n\n    // Wait for file to appear in results\n    const fileResult = await this.browser.$('.quick-input-list-row');\n    await fileResult.waitForExist({ timeout: 10000, timeoutMsg: 'Quick file open results did not appear. Ripgrep may not be working correctly with asar packaging.' });\n\n    // Verify README.md appears in results\n    const resultLabel = await this.browser.$('.quick-input-list-label');\n    const labelText = await resultLabel.getText();\n    expect(labelText.toLowerCase()).to.include('readme');\n  });\n\n  it('Integrated terminal', async function () {\n    // Wait a bit to make sure key handlers are registered\n    await new Promise(r => setTimeout(r, 5000));\n\n    // Create a new terminal (Ctrl+Shift+`) to ensure it is focused and visible,\n    // even when the terminal manager uses tabbed layout in the bottom panel.\n    await this.browser.keys(['Control', 'Shift', '`']);\n\n    // Wait for terminal widget to appear\n    const terminal = await this.browser.$('.xterm');\n    await terminal.waitForExist({ timeout: 10000, timeoutMsg: 'Terminal did not open. PTY may not be working correctly with asar packaging.' });\n    await terminal.waitForDisplayed();\n\n    // Verify terminal is visible\n    const isDisplayed = await terminal.isDisplayed();\n    expect(isDisplayed).to.equal(true);\n  });\n});\n"
  },
  {
    "path": "applications/electron/test/workspace/README.md",
    "content": "# Test Workspace\n\nThis is the test workspace for E2E tests.\n"
  },
  {
    "path": "applications/electron/tsconfig.eslint.json",
    "content": "{\n    \"extends\": \"./tsconfig.json\",\n    \"compilerOptions\": {\n        \"noEmit\": true\n    },\n    \"include\": [\n      \"./scripts\",\n      \"./test\"\n    ]\n  }\n  "
  },
  {
    "path": "applications/electron/tsconfig.json",
    "content": "{\n    \"extends\": \"../../configs/base.tsconfig\",\n    \"include\": [],\n    \"compilerOptions\": {\n      \"composite\": true,\n      \"esModuleInterop\": true\n    },\n    \"references\": [\n      {\n        \"path\": \"../../theia-extensions/launcher\"\n      },\n      {\n        \"path\": \"../../theia-extensions/product\"\n      },\n      {\n        \"path\": \"../../theia-extensions/updater\"\n      }\n    ]\n}\n"
  },
  {
    "path": "applications/electron/webpack.config.js",
    "content": "/**\n * This file can be edited to customize webpack configuration.\n * To reset delete this file and rerun theia build again.\n */\n// @ts-check\nconst configs = require('./gen-webpack.config.js');\nconst nodeConfig = require('./gen-webpack.node.config.js');\nconst TerserPlugin = require('terser-webpack-plugin');\nconst fs = require('fs');\nconst path = require('path');\n\n/**\n * Webpack plugin to patch the bundled ripgrep path for asar compatibility.\n * When packaged with asar, __dirname resolves inside app.asar but the native binaries\n * are extracted to app.asar.unpacked via asarUnpack.\n *\n * The native-webpack-plugin bundles ripgrep path resolution directly into main.js,\n * so we need to patch the bundle after emit to add asar path rewriting.\n */\nclass PatchRipgrepPlugin {\n    apply(compiler) {\n        compiler.hooks.afterEmit.tapAsync('PatchRipgrepPlugin', (compilation, callback) => {\n            const mainJsPath = path.join(compiler.outputPath, 'main.js');\n            if (fs.existsSync(mainJsPath)) {\n                let content = fs.readFileSync(mainJsPath, 'utf8');\n\n                // Match the ripgrep path.join(__dirname, ...) call regardless of how\n                // the path module is referenced or how the result is exported.\n                // Webpack output varies across modes:\n                //   Production (minified):  i.join(__dirname,\"./native/rg\"+(\"win32\"===...))\n                //   Dev (harmony):          (__webpack_require__(/*! path */ \"path\").join)(__dirname, `./native/rg${...}`)\n                //   Dev (CommonJS):         path.join(__dirname, `./native/rg${...}`)\n                // The .join call may be direct (EXPR.join(...)) or parenthesized ((EXPR.join)(...)).\n                // Both string concat (prod) and template literal (dev) arg forms are matched.\n                const rgSuffix = `(\"win32\"===process.platform?\".exe\":\"\")`;\n                const prodArgs = /[\"']\\.\\/native\\/rg[\"']\\s*\\+\\s*\\([\"']win32[\"']\\s*===\\s*process\\.platform\\s*\\?\\s*[\"']\\.exe[\"']\\s*:\\s*[\"'][\"']\\s*\\)/;\n                const devArgs = /`\\.\\/native\\/rg\\$\\{process\\.platform\\s*===\\s*['\"]win32['\"]\\s*\\?\\s*['\"]\\.exe['\"]\\s*:\\s*['\"]['\"]}\\s*`/;\n                // Match both EXPR.join(__dirname, ARGS) and (EXPR.join)(__dirname, ARGS)\n                // Use [^=,;\\n] (not \\s) to allow spaces in webpack comments like /*! path */\n                const joinCall = (argsPattern) => new RegExp(`\\\\(?[^=,;\\\\n]+?\\\\.join\\\\)?\\\\(\\\\s*__dirname\\\\s*,\\\\s*${argsPattern.source}\\\\s*\\\\)`, 'g');\n                const prodPattern = joinCall(prodArgs);\n                const devPattern = joinCall(devArgs);\n\n                let patched = false;\n                const patchFn = (match) => {\n                    patched = true;\n                    return `(()=>{const _p=require(\"path\"),_r=_p.join(__dirname,\"./native/rg\"+${rgSuffix});return _r.includes(\".asar\"+_p.sep)?_r.replace(\".asar\"+_p.sep,\".asar.unpacked\"+_p.sep):_r})()`;\n                };\n\n                let newContent = content.replace(prodPattern, patchFn);\n                if (!patched) {\n                    newContent = content.replace(devPattern, patchFn);\n                }\n\n                if (patched) {\n                    fs.writeFileSync(mainJsPath, newContent);\n                    console.log('Patched main.js ripgrep path for asar compatibility');\n                } else {\n                    const idx = content.indexOf('rgPath');\n                    if (idx !== -1) {\n                        console.error('Could not patch ripgrep path. Context around rgPath:');\n                        console.error(content.substring(Math.max(0, idx - 200), idx + 300));\n                    }\n                    throw new Error('Could not find ripgrep pattern to patch in main.js. The pattern may have changed in @theia/native-webpack-plugin.');\n                }\n            }\n            callback();\n        });\n    }\n}\n\n/**\n * Expose bundled modules on window.theia.moduleName namespace, e.g.\n * window['theia']['@theia/core/lib/common/uri'].\n * Such syntax can be used by external code, for instance, for testing.\nconfigs[0].module.rules.push({\n    test: /\\.js$/,\n    loader: require.resolve('@theia/application-manager/lib/expose-loader')\n}); */\n\n/**\n * Do no run TerserPlugin with parallel: true\n * Each spawned node may take the full memory configured via NODE_OPTIONS / --max_old_space_size\n * In total this may lead to OOM issues\n */\nif (nodeConfig.config.optimization) {\n    nodeConfig.config.optimization.minimizer = [\n        new TerserPlugin({\n            parallel: false,\n            exclude: /^(lib|builtins)\\//,\n            terserOptions: {\n                keep_classnames: /AbortSignal/\n            }\n        })\n    ];\n}\nfor (const config of configs) {\n    config.optimization = {\n        minimizer: [\n            new TerserPlugin({\n                parallel: false\n            })\n        ]\n    };\n}\n\n// Add the ripgrep patch plugin to the node config\nnodeConfig.config.plugins = nodeConfig.config.plugins || [];\nnodeConfig.config.plugins.push(new PatchRipgrepPlugin());\n\nmodule.exports = [\n    ...configs,\n    nodeConfig.config\n];"
  },
  {
    "path": "applications/electron-next/.eslintrc.js",
    "content": "/** @type {import('eslint').Linter.Config} */\nmodule.exports = {\n    extends: [\n        '../../configs/build.eslintrc.json'\n    ],\n    parserOptions: {\n        tsconfigRootDir: __dirname,\n        project: 'tsconfig.eslint.json'\n    }\n};\n"
  },
  {
    "path": "applications/electron-next/electron-builder.yml",
    "content": "appId: eclipse.theia.next\nproductName: TheiaIDENext\ncopyright: Copyright © 2020-2025 Eclipse Foundation, Inc\nelectronDist: ../../node_modules/electron/dist\nelectronVersion: 39.8.7\nasar: true\nasarUnpack:\n  - \"**/lib/backend/native/**\"\n  - \"**/lib/backend/shell-integrations/**\"\n  - \"**/lib/build/Release/**\"\n  - \"**/lib/prebuilds/**\"\nnodeGypRebuild: false\nnpmRebuild: false\n\ndirectories:\n  buildResources: resources\n\n# node_modules and package.json are copied automatically\n# Exclude node_modules manually because electron is copied by electron-builder and we are using a bundled backend\nfiles:\n  - src-gen\n  - lib\n  - resources/icons/WindowIcon/512-512.png\n  - resources/TheiaIDENextSplash.svg\n  - scripts\n  - \"!**node_modules/**\"\nextraResources:\n  - from: ../../plugins\n    to: app/plugins\n\nlinux:\n  icon: resources/icons/LinuxLauncherIcons\n  category: Development\n  mimeTypes:\n    - inode/directory\n  vendor: Eclipse Foundation, Inc\n  target:\n    - deb\n    - AppImage\n\ndeb:\n  artifactName: ${productName}.${ext}\nappImage:\n  artifactName: ${productName}.${ext}\n\nafterPack: ./scripts/after-pack.js\n"
  },
  {
    "path": "applications/electron-next/package.json",
    "content": "{\n  \"private\": true,\n  \"name\": \"theia-ide-next-electron-app\",\n  \"description\": \"Eclipse Theia IDE Next product\",\n  \"productName\": \"Theia IDE Next\",\n  \"version\": \"1.71.100\",\n  \"main\": \"scripts/theia-electron-main.js\",\n  \"license\": \"MIT\",\n  \"author\": \"Eclipse Theia <theia-dev@eclipse.org>\",\n  \"homepage\": \"https://github.com/eclipse-theia/theia-ide#readme\",\n  \"bugs\": {\n    \"url\": \"https://github.com/eclipse-theia/theia/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/eclipse-theia/theia-ide.git\"\n  },\n  \"engines\": {\n    \"yarn\": \">=1.7.0 <2\",\n    \"node\": \">=22\"\n  },\n  \"theia\": {\n    \"target\": \"electron\",\n    \"frontend\": {\n      \"config\": {\n        \"applicationName\": \"Theia IDE Next\",\n        \"brandingVariant\": \"next\",\n        \"availableUpdateChannels\": [\n          \"next\"\n        ],\n        \"reloadOnReconnect\": true,\n        \"preferences\": {\n          \"toolbar.showToolbar\": true,\n          \"updates.channel\": \"next\",\n          \"ai-features.chat.tokenUsageIndicator.enabled\": true\n        },\n        \"electron\": {\n          \"uriScheme\": \"theia-next\",\n          \"showWindowEarly\": false,\n          \"splashScreenOptions\": {\n            \"content\": \"resources/TheiaIDENextSplash.svg\",\n            \"height\": 276,\n            \"width\": 446\n          }\n        }\n      }\n    },\n    \"backend\": {\n      \"config\": {\n        \"frontendConnectionTimeout\": -1,\n        \"startupTimeout\": -1,\n        \"resolveSystemPlugins\": false,\n        \"configurationFolder\": \".theia-ide-next\"\n      }\n    },\n    \"generator\": {\n      \"config\": {\n        \"preloadTemplate\": \"./resources/preload.html\"\n      }\n    }\n  },\n  \"dependencies\": {\n    \"@theia/ai-anthropic\": \"1.72.0-next.20\",\n    \"@theia/ai-chat\": \"1.72.0-next.20\",\n    \"@theia/ai-chat-ui\": \"1.72.0-next.20\",\n    \"@theia/ai-claude-code\": \"1.72.0-next.20\",\n    \"@theia/ai-code-completion\": \"1.72.0-next.20\",\n    \"@theia/ai-codex\": \"1.72.0-next.20\",\n    \"@theia/ai-copilot\": \"1.72.0-next.20\",\n    \"@theia/ai-core\": \"1.72.0-next.20\",\n    \"@theia/ai-core-ui\": \"1.72.0-next.20\",\n    \"@theia/ai-editor\": \"1.72.0-next.20\",\n    \"@theia/ai-google\": \"1.72.0-next.20\",\n    \"@theia/ai-history\": \"1.72.0-next.20\",\n    \"@theia/ai-huggingface\": \"1.72.0-next.20\",\n    \"@theia/ai-ide\": \"1.72.0-next.20\",\n    \"@theia/ai-llamafile\": \"1.72.0-next.20\",\n    \"@theia/ai-mcp\": \"1.72.0-next.20\",\n    \"@theia/ai-mcp-server\": \"1.72.0-next.20\",\n    \"@theia/ai-mcp-ui\": \"1.72.0-next.20\",\n    \"@theia/ai-ollama\": \"1.72.0-next.20\",\n    \"@theia/ai-openai\": \"1.72.0-next.20\",\n    \"@theia/ai-scanoss\": \"1.72.0-next.20\",\n    \"@theia/ai-terminal\": \"1.72.0-next.20\",\n    \"@theia/ai-vercel-ai\": \"1.72.0-next.20\",\n    \"@theia/bulk-edit\": \"1.72.0-next.20\",\n    \"@theia/callhierarchy\": \"1.72.0-next.20\",\n    \"@theia/collaboration\": \"1.72.0-next.20\",\n    \"@theia/console\": \"1.72.0-next.20\",\n    \"@theia/core\": \"1.72.0-next.20\",\n    \"@theia/debug\": \"1.72.0-next.20\",\n    \"@theia/dev-container\": \"1.72.0-next.20\",\n    \"@theia/editor\": \"1.72.0-next.20\",\n    \"@theia/editor-preview\": \"1.72.0-next.20\",\n    \"@theia/electron\": \"1.72.0-next.20\",\n    \"@theia/external-terminal\": \"1.72.0-next.20\",\n    \"@theia/file-search\": \"1.72.0-next.20\",\n    \"@theia/filesystem\": \"1.72.0-next.20\",\n    \"@theia/getting-started\": \"1.72.0-next.20\",\n    \"@theia/keymaps\": \"1.72.0-next.20\",\n    \"@theia/markers\": \"1.72.0-next.20\",\n    \"@theia/memory-inspector\": \"1.72.0-next.20\",\n    \"@theia/messages\": \"1.72.0-next.20\",\n    \"@theia/metrics\": \"1.72.0-next.20\",\n    \"@theia/mini-browser\": \"1.72.0-next.20\",\n    \"@theia/monaco\": \"1.72.0-next.20\",\n    \"@theia/navigator\": \"1.72.0-next.20\",\n    \"@theia/notebook\": \"1.72.0-next.20\",\n    \"@theia/outline-view\": \"1.72.0-next.20\",\n    \"@theia/output\": \"1.72.0-next.20\",\n    \"@theia/plugin-dev\": \"1.72.0-next.20\",\n    \"@theia/plugin-ext\": \"1.72.0-next.20\",\n    \"@theia/plugin-ext-vscode\": \"1.72.0-next.20\",\n    \"@theia/preferences\": \"1.72.0-next.20\",\n    \"@theia/preview\": \"1.72.0-next.20\",\n    \"@theia/process\": \"1.72.0-next.20\",\n    \"@theia/property-view\": \"1.72.0-next.20\",\n    \"@theia/remote\": \"1.72.0-next.20\",\n    \"@theia/remote-wsl\": \"1.72.0-next.20\",\n    \"@theia/scanoss\": \"1.72.0-next.20\",\n    \"@theia/scm\": \"1.72.0-next.20\",\n    \"@theia/search-in-workspace\": \"1.72.0-next.20\",\n    \"@theia/secondary-window\": \"1.72.0-next.20\",\n    \"@theia/task\": \"1.72.0-next.20\",\n    \"@theia/terminal\": \"1.72.0-next.20\",\n    \"@theia/terminal-manager\": \"1.72.0-next.20\",\n    \"@theia/test\": \"1.72.0-next.20\",\n    \"@theia/timeline\": \"1.72.0-next.20\",\n    \"@theia/toolbar\": \"1.72.0-next.20\",\n    \"@theia/typehierarchy\": \"1.72.0-next.20\",\n    \"@theia/userstorage\": \"1.72.0-next.20\",\n    \"@theia/variable-resolver\": \"1.72.0-next.20\",\n    \"@theia/vsx-registry\": \"1.72.0-next.20\",\n    \"@theia/workspace\": \"1.72.0-next.20\",\n    \"fs-extra\": \"^9.1.0\",\n    \"theia-ide-launcher-ext\": \"1.71.100\",\n    \"theia-ide-product-ext\": \"1.71.100\",\n    \"theia-ide-updater-ext\": \"1.71.100\"\n  },\n  \"devDependencies\": {\n    \"@theia/cli\": \"1.72.0-next.20\",\n    \"@theia/bundle-plugin\": \"1.72.0-next.20\",\n    \"@types/js-yaml\": \"^3.12.10\",\n    \"@types/yargs\": \"17.0.7\",\n    \"@wdio/cli\": \"^6.12.1\",\n    \"@wdio/local-runner\": \"^6.12.1\",\n    \"@wdio/mocha-framework\": \"^6.11.0\",\n    \"@wdio/spec-reporter\": \"^6.11.0\",\n    \"app-builder-lib\": \"26.0.12\",\n    \"chai\": \"^4.5.0\",\n    \"concurrently\": \"^3.6.1\",\n    \"electron\": \"39.8.7\",\n    \"electron-builder\": \"26.0.12\",\n    \"electron-chromedriver\": \"^28.3.3\",\n    \"electron-mocha\": \"^12.3.1\",\n    \"electron-osx-sign\": \"^0.6.0\",\n    \"js-yaml\": \"^3.14.2\",\n    \"mocha\": \"^8.4.0\",\n    \"rimraf\": \"^2.7.1\",\n    \"ts-node\": \"^10.9.2\",\n    \"wdio-chromedriver-service\": \"^6.0.4\",\n    \"webdriverio\": \"^6.12.1\",\n    \"yargs\": \"17.2.1\"\n  },\n  \"scripts\": {\n    \"clean\": \"theia clean && rimraf node_modules\",\n    \"clean:dist\": \"rimraf dist\",\n    \"build\": \"yarn -s rebuild && theia build --app-target=\\\"electron\\\" --mode development\",\n    \"build:prod\": \"yarn -s rebuild && theia build --app-target=\\\"electron\\\"\",\n    \"rebuild\": \"theia rebuild:electron --cacheRoot ../..\",\n    \"watch\": \"concurrently -n compile,build \\\"theiaext watch --preserveWatchOutput\\\" \\\"theia build --watch --mode development\\\"\",\n    \"start\": \"electron scripts/theia-electron-main.js --plugins=local-dir:../../plugins\",\n    \"start:debug\": \"yarn start --log-level=debug\",\n    \"package\": \"yarn clean:dist && yarn rebuild && electron-builder -c.mac.identity=null --publish never\",\n    \"package:prod\": \"yarn deploy\",\n    \"deploy\": \"yarn clean:dist && yarn rebuild && electron-builder -c.mac.identity=null --publish always\",\n    \"package:preview\": \"yarn clean:dist && yarn rebuild && electron-builder -c.mac.identity=null --dir\",\n    \"update:theia\": \"ts-node ../../scripts/update-theia-version.ts\",\n    \"update:next\": \"ts-node ../../scripts/update-theia-version.ts next\",\n    \"test\": \"mocha --timeout 60000 \\\"../electron/test/*.spec.js\\\"\",\n    \"lint\": \"eslint --ext js,jsx,ts,tsx scripts\",\n    \"lint:fix\": \"eslint --ext js,jsx,ts,tsx scripts --fix\"\n  }\n}"
  },
  {
    "path": "applications/electron-next/resources/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2026 Eclipse Theia IDE Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "applications/electron-next/resources/preload.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <style>\n        html,\n        body {\n            background-color: black;\n        }\n\n        .theia-preload {\n            position: absolute;\n            top: 0;\n            left: 0;\n            right: 0;\n            bottom: 0;\n            /* Above styles copied from https://github.com/eclipse-theia/theia/blob/5aeef6c0c683b4e91713ab736957e6655b486adc/packages/core/src/browser/style/index.css#L147-L151 */\n            /* Otherwise, there is a flickering when Theia's CSS loads. */\n\n            background-image: none;\n        }\n\n        .theia-preload::after {\n            /* remove default loading animation */\n            content: none;\n        }\n\n        .spinner-container {\n            display: flex;\n            flex-direction: center;\n            align-self: center;\n            justify-content: center;\n            height: 100vh;\n            width: 100vw;\n        }\n\n        .custom-spinner {\n            align-self: center;\n        }\n\n        .custom-spinner svg {\n            width: 16vw;\n            height: 16vh;\n            animation-delay: 0;\n            animation-duration: 2s;\n            animation-iteration-count: infinite;\n            animation-name: theia-ide-spinner;\n            animation-timing-function: ease;\n        }\n\n        @keyframes theia-ide-spinner {\n            0% {\n                filter: brightness(1) saturate(1);\n                transform: scale(1.0);\n            }\n\n            50% {\n                filter: brightness(1.35) saturate(0.75);\n                transform: scale(0.8);\n            }\n\n            100% {\n                filter: brightness(1) saturate(1);\n                transform: scale(1.0);\n            }\n        }\n    </style>\n</head>\n\n<body>\n    <div class='spinner-container'>\n        <div class='custom-spinner'>\n            <svg id=\"spinner\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"\n                xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0\" y=\"0\" preserveAspectRatio=\"xMinYMin meet\"\n                viewBox=\"0, 0, 1150, 540.6\">\n                <g id=\"Layer_1\" fill=\"#8B5CF6\">\n                    <path\n                        d=\"M880.199,2.8 C1028.1,2.8 1147.9,122.6 1147.9,270.5 C1147.9,418.3 1028.1,538.2 880.2,538.2 L290.1,538.2 C269,538.2 251.9,521.1 251.9,500 C251.9,478.9 269,461.8 290.1,461.8 L427.6,461.8 C448.6,461.8 465.7,444.7 465.7,423.6 C465.7,402.5 448.6,385.4 427.6,385.4 L396.999,385.4 C375.9,385.4 358.8,368.3 358.8,347.2 C358.8,326.1 375.9,309 397,309 L488.703,309 C509.918,308.941 526.373,291.65 526.9,270.8 C526.9,249.7 509.8,232.6 488.7,232.6 L167.8,232.6 C146.7,232.6 129.6,215.5 129.6,194.4 C129.6,173.3 146.7,156.2 167.8,156.2 L404.604,156.2 C425.818,156.141 442.273,138.85 442.8,118 C442.8,96.9 425.7,79.8 404.6,79.8 L351.2,79.8 C330.1,79.8 313,62.7 313,41.6 C313,20.5 330.1,2.4 351.2,2.4 L880.199,2.8 z M837.4,92 L837.4,92 C755.2,92 688.7,158.6 688.7,240.7 L688.7,300.2 C688.7,382.4 755.2,448.9 837.4,448.9 C919.5,448.9 986.1,382.4 986.1,300.2 L986.1,240.7 C986.1,158.6 919.5,92 837.4,92 L837.4,92 z M888.2,232.6 C908,232.6 924.1,248.7 924.1,268.5 L924.1,273.1 C924.1,292.9 908,309 888.2,309 L776.6,309 C756.8,309 740.7,292.9 740.7,273.1 L740.7,268.5 C740.7,248.7 756.8,232.6 776.6,232.6 L888.2,232.6 z\" />\n                    <path\n                        d=\"M170.1,461.8 C190,461.8 206,477.8 206,497.7 L206,502.3 C206,522.1 190,538.2 170.1,538.2 L38,538.2 C18.2,538.2 2.1,522.1 2.1,502.3 L2.1,497.7 C2.1,477.8 18.2,461.8 38,461.8 L170.1,461.8 z\" />\n                    <path\n                        d=\"M231.3,3.4 C251.1,3.4 267.1,19.5 267.1,39.3 L267.1,44 C267.1,63.8 251.1,79.8 231.3,79.8 L83.8,79.8 C64,79.8 47.9,63.8 47.9,44 L47.9,39.3 C47.9,19.5 64,3.4 83.8,3.4 L231.3,3.4 z\" />\n                    <path\n                        d=\"M277.1,309 C296.9,309 313,325.1 313,344.9 L313,349.5 C313,369.3 296.9,385.4 277.1,385.4 L196.1,385.4 C176.3,385.4 160.2,369.3 160.2,349.5 L160.2,344.9 C160.2,325.1 176.3,309 196.1,309 L277.1,309 z\" />\n                </g>\n            </svg>\n        </div>\n    </div>\n</body>\n\n</html>\n"
  },
  {
    "path": "applications/electron-next/scripts/after-pack.js",
    "content": "#!/usr/bin/env node\n\nconst fs = require('fs');\nconst path = require('path');\n\n// Signing and notarizing are not needed for the Next product\n// (it is not built on Jenkins as a full release).\n// Only the Linux sandbox fix is required for AppImage builds.\n\n// taken and modified from: https://github.com/gergof/electron-builder-sandbox-fix/blob/a2251d7d8f22be807d2142da0cf768c78d4cfb0a/lib/index.js\nexports.default = async function (context) {\n    if (context.electronPlatformName !== 'linux') {\n        return;\n    }\n    const executable = path.join(\n        context.appOutDir,\n        context.packager.executableName\n    );\n\n    const loaderScript = `#!/usr/bin/env bash\nset -u\nSCRIPT_DIR=\"$( cd \"$( dirname \"\\${BASH_SOURCE[0]}\" )\" && pwd )\"\nexec \"$SCRIPT_DIR/${context.packager.executableName}.bin\" \"--no-sandbox\" \"$@\"\n`;\n\n    try {\n        await fs.promises.rename(executable, executable + '.bin');\n        await fs.promises.writeFile(executable, loaderScript);\n        await fs.promises.chmod(executable, 0o755);\n    } catch (e) {\n        throw new Error('Failed to create loader for sandbox fix:\\n' + e);\n    }\n};\n"
  },
  {
    "path": "applications/electron-next/scripts/appimage-helpers.js",
    "content": "const fs = require('fs');\nconst path = require('path');\n\n/**\n * Reads the plugin copy metadata file and returns its content.\n * @param metadataPath - Path to the metadata file\n * @returns The metadata object or undefined if not found\n */\nfunction readPluginCopyMetadata(metadataPath) {\n    if (!fs.existsSync(metadataPath)) {\n        return undefined;\n    }\n    try {\n        return JSON.parse(fs.readFileSync(metadataPath, 'utf8'));\n    } catch (err) {\n        console.warn('Could not read built-in plugin copy metadata file:', err.message);\n        return undefined;\n    }\n}\n\n/**\n * Writes the plugin copy metadata file with version and timestamp.\n * @param metadataPath - Path to the metadata file\n * @param version - Current version\n */\nfunction writePluginCopyMetadata(metadataPath, version) {\n    const metadata = {\n        version: version,\n        copiedAt: new Date().toISOString()\n    };\n    fs.writeFileSync(metadataPath, JSON.stringify(metadata, undefined, 2));\n}\n\n/**\n * Copies bundled plugins from AppImage to user directory if needed.\n * @param bundledPluginsDir - Path to bundled plugins in AppImage\n * @param userPluginsDir - Path to user built-in plugins directory\n * @param currentVersion - Current Theia IDE version\n * @returns true if the builtins were copied to the user dir, false if there was an error\n */\nfunction copyBundledPlugins(bundledPluginsDir, userPluginsDir, currentVersion) {\n    const metadataFile = path.join(userPluginsDir, '.builtInPlugins-metadata');\n\n    // Ensure the user plugins directory exists\n    if (!fs.existsSync(userPluginsDir)) {\n        fs.mkdirSync(userPluginsDir, { recursive: true });\n    }\n\n    // Check if built-in plugins need to be copied\n    const metadata = readPluginCopyMetadata(metadataFile);\n    let shouldCopy = false;\n\n    if (!metadata) {\n        shouldCopy = true;\n    } else if (metadata.version !== currentVersion) {\n        console.log(`Theia IDE updated from ${metadata.version} to ${currentVersion}. Updating built-in plugins...`);\n        shouldCopy = true;\n    }\n\n    if (!shouldCopy) {\n        console.log('Built-in plugins were already copied.');\n        return true;\n    }\n\n    console.log(`Copying bundled plugins from AppImage to ${userPluginsDir}...`);\n    try {\n        // Clean existing plugins directory to remove old/obsolete plugins\n        fs.rmSync(userPluginsDir, { recursive: true, force: true });\n        fs.mkdirSync(userPluginsDir, { recursive: true });\n\n        const pluginEntries = fs.readdirSync(bundledPluginsDir, { withFileTypes: true });\n        for (const entry of pluginEntries) {\n            const srcPath = path.join(bundledPluginsDir, entry.name);\n            const destPath = path.join(userPluginsDir, entry.name);\n            fs.cpSync(srcPath, destPath, { recursive: true });\n        }\n        writePluginCopyMetadata(metadataFile, currentVersion);\n        console.log(`Bundled plugins copied successfully to ${userPluginsDir}.`);\n    } catch (err) {\n        console.error('Failed to copy bundled plugins:', err.message);\n        return false;\n    }\n    return true;\n}\n\nmodule.exports = {\n    copyBundledPlugins,\n};\n"
  },
  {
    "path": "applications/electron-next/scripts/theia-electron-main.js",
    "content": "const path = require('path');\nconst fs = require('fs');\nconst os = require('os');\nconst { copyBundledPlugins } = require('./appimage-helpers');\n\n// Update to override the supported VS Code API version.\n// process.env.VSCODE_API_VERSION = '1.50.0'\n\n// Detect if running as AppImage\nconst isAppImage = !!process.env.APPIMAGE;\n\n// When packaged with asar, __dirname is inside app.asar (e.g., .../app.asar/scripts)\n// but plugins are in extraResources at .../app/plugins (outside the asar)\nconst isInsideAsar = __dirname.includes('.asar');\nconst bundledPluginsDir = isInsideAsar\n    ? path.join(process.resourcesPath, 'app', 'plugins')\n    : path.resolve(__dirname, '../', 'plugins');\n\nif (isAppImage) {\n    // When running as AppImage, use a user-writable directory for the built-in plugins\n    // The AppImage mount point (/tmp/.mount_*) is read-only\n    const configDir = process.env.THEIA_CONFIG_DIR || path.join(os.homedir(), '.theia-ide-next');\n    const userPluginsDir = path.join(configDir, 'builtInPlugins');\n    const packageJsonPath = path.resolve(__dirname, '../', 'package.json');\n    const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));\n    const currentVersion = packageJson.version;\n\n    // Copy bundled plugins to user directory if needed (first run or version update)\n    const useUserDir = copyBundledPlugins(bundledPluginsDir, userPluginsDir, currentVersion);\n    // If copying fails, fall back to the read-only bundled directory (will be improved in follow up of GH-630)\n    process.env.THEIA_DEFAULT_PLUGINS = `local-dir:${useUserDir ? userPluginsDir : bundledPluginsDir}`;\n\n} else {\n    // Use a set of builtin plugins in our application.\n    process.env.THEIA_DEFAULT_PLUGINS = `local-dir:${bundledPluginsDir}`;\n}\n\n// Handover to the auto-generated electron application handler.\nrequire('../lib/backend/electron-main.js');\n"
  },
  {
    "path": "applications/electron-next/test/workspace/README.md",
    "content": "# Test Workspace\n\nThis is the test workspace for E2E tests.\n"
  },
  {
    "path": "applications/electron-next/tsconfig.eslint.json",
    "content": "{\n    \"extends\": \"./tsconfig.json\",\n    \"compilerOptions\": {\n        \"noEmit\": true\n    },\n    \"include\": [\n      \"./scripts\"\n    ]\n  }\n"
  },
  {
    "path": "applications/electron-next/tsconfig.json",
    "content": "{\n    \"extends\": \"../../configs/base.tsconfig\",\n    \"include\": [],\n    \"compilerOptions\": {\n      \"composite\": true,\n      \"esModuleInterop\": true\n    },\n    \"references\": [\n      {\n        \"path\": \"../../theia-extensions/product\"\n      },\n      {\n        \"path\": \"../../theia-extensions/updater\"\n      }\n    ]\n}\n"
  },
  {
    "path": "applications/electron-next/webpack.config.js",
    "content": "/**\n * This file can be edited to customize webpack configuration.\n * To reset delete this file and rerun theia build again.\n */\n// @ts-check\nconst configs = require('./gen-webpack.config.js');\nconst nodeConfig = require('./gen-webpack.node.config.js');\nconst TerserPlugin = require('terser-webpack-plugin');\nconst fs = require('fs');\nconst path = require('path');\n\n/**\n * Webpack plugin to patch the bundled ripgrep path for asar compatibility.\n * When packaged with asar, __dirname resolves inside app.asar but the native binaries\n * are extracted to app.asar.unpacked via asarUnpack.\n *\n * The native-webpack-plugin bundles ripgrep path resolution directly into main.js,\n * so we need to patch the bundle after emit to add asar path rewriting.\n */\nclass PatchRipgrepPlugin {\n    apply(compiler) {\n        compiler.hooks.afterEmit.tapAsync('PatchRipgrepPlugin', (compilation, callback) => {\n            const mainJsPath = path.join(compiler.outputPath, 'main.js');\n            if (fs.existsSync(mainJsPath)) {\n                let content = fs.readFileSync(mainJsPath, 'utf8');\n\n                // Match the ripgrep path.join(__dirname, ...) call regardless of how\n                // the path module is referenced or how the result is exported.\n                // Webpack output varies across modes:\n                //   Production (minified):  i.join(__dirname,\"./native/rg\"+(\"win32\"===...))\n                //   Dev (harmony):          (__webpack_require__(/*! path */ \"path\").join)(__dirname, `./native/rg${...}`)\n                //   Dev (CommonJS):         path.join(__dirname, `./native/rg${...}`)\n                // The .join call may be direct (EXPR.join(...)) or parenthesized ((EXPR.join)(...)).\n                // Both string concat (prod) and template literal (dev) arg forms are matched.\n                const rgSuffix = `(\"win32\"===process.platform?\".exe\":\"\")`;\n                const prodArgs = /[\"']\\.\\/native\\/rg[\"']\\s*\\+\\s*\\([\"']win32[\"']\\s*===\\s*process\\.platform\\s*\\?\\s*[\"']\\.exe[\"']\\s*:\\s*[\"'][\"']\\s*\\)/;\n                const devArgs = /`\\.\\/native\\/rg\\$\\{process\\.platform\\s*===\\s*['\"]win32['\"]\\s*\\?\\s*['\"]\\.exe['\"]\\s*:\\s*['\"]['\"]}\\s*`/;\n                // Match both EXPR.join(__dirname, ARGS) and (EXPR.join)(__dirname, ARGS)\n                // Use [^=,;\\n] (not \\s) to allow spaces in webpack comments like /*! path */\n                const joinCall = (argsPattern) => new RegExp(`\\\\(?[^=,;\\\\n]+?\\\\.join\\\\)?\\\\(\\\\s*__dirname\\\\s*,\\\\s*${argsPattern.source}\\\\s*\\\\)`, 'g');\n                const prodPattern = joinCall(prodArgs);\n                const devPattern = joinCall(devArgs);\n\n                let patched = false;\n                const patchFn = (match) => {\n                    patched = true;\n                    return `(()=>{const _p=require(\"path\"),_r=_p.join(__dirname,\"./native/rg\"+${rgSuffix});return _r.includes(\".asar\"+_p.sep)?_r.replace(\".asar\"+_p.sep,\".asar.unpacked\"+_p.sep):_r})()`;\n                };\n\n                let newContent = content.replace(prodPattern, patchFn);\n                if (!patched) {\n                    newContent = content.replace(devPattern, patchFn);\n                }\n\n                if (patched) {\n                    fs.writeFileSync(mainJsPath, newContent);\n                    console.log('Patched main.js ripgrep path for asar compatibility');\n                } else {\n                    const idx = content.indexOf('rgPath');\n                    if (idx !== -1) {\n                        console.error('Could not patch ripgrep path. Context around rgPath:');\n                        console.error(content.substring(Math.max(0, idx - 200), idx + 300));\n                    }\n                    throw new Error('Could not find ripgrep pattern to patch in main.js. The pattern may have changed in @theia/native-webpack-plugin.');\n                }\n            }\n            callback();\n        });\n    }\n}\n\n/**\n * Expose bundled modules on window.theia.moduleName namespace, e.g.\n * window['theia']['@theia/core/lib/common/uri'].\n * Such syntax can be used by external code, for instance, for testing.\nconfigs[0].module.rules.push({\n    test: /\\.js$/,\n    loader: require.resolve('@theia/application-manager/lib/expose-loader')\n}); */\n\n/**\n * Do no run TerserPlugin with parallel: true\n * Each spawned node may take the full memory configured via NODE_OPTIONS / --max_old_space_size\n * In total this may lead to OOM issues\n */\nif (nodeConfig.config.optimization) {\n    nodeConfig.config.optimization.minimizer = [\n        new TerserPlugin({\n            parallel: false,\n            exclude: /^(lib|builtins)\\//,\n            terserOptions: {\n                keep_classnames: /AbortSignal/\n            }\n        })\n    ];\n}\nfor (const config of configs) {\n    config.optimization = {\n        minimizer: [\n            new TerserPlugin({\n                parallel: false\n            })\n        ]\n    };\n}\n\n// Add the ripgrep patch plugin to the node config\nnodeConfig.config.plugins = nodeConfig.config.plugins || [];\nnodeConfig.config.plugins.push(new PatchRipgrepPlugin());\n\nmodule.exports = [\n    ...configs,\n    nodeConfig.config\n];\n"
  },
  {
    "path": "browser.Dockerfile",
    "content": "# Builder stage\nFROM node:24-bookworm AS build-stage\n\n# install required tools to build the application\nRUN apt-get update && apt-get install -y libxkbfile-dev libsecret-1-dev\n\nWORKDIR /home/theia\n\n# Copy repository files\nCOPY . .\n\n# Remove unnecesarry files for the browser application\n# Download plugins and build application production mode\n# Use yarn autoclean to remove unnecessary files from package dependencies\nRUN yarn config set network-timeout 600000 -g && \\\n    yarn --pure-lockfile && \\\n    yarn build:extensions && \\\n    yarn download:plugins && \\\n    yarn browser build && \\\n    yarn && \\\n    yarn autoclean --init && \\\n    echo *.ts >> .yarnclean && \\\n    echo *.ts.map >> .yarnclean && \\\n    echo *.spec.* >> .yarnclean && \\\n    yarn autoclean --force && \\\n    yarn cache clean && \\\n    rm -rf .git applications/electron theia-extensions/launcher theia-extensions/updater node_modules\n\n# Production stage uses a small base image\nFROM node:24-bookworm-slim AS production-stage\n\n# Create theia user and directories\n# Application will be copied to /home/theia\n# Default workspace is located at /home/project\nRUN adduser --system --group --home /home/theia theia\nRUN chmod g+rw /home && \\\n    mkdir -p /home/project && \\\n    chown -R theia:theia /home/theia && \\\n    chown -R theia:theia /home/project;\n\n# Install required tools for application: Temurin JDK, JDK, SSH, Bash, Maven\n# Node is already available in base image\nRUN apt-get update && apt-get install -y wget apt-transport-https && \\\n    apt-get update && apt-get install -y git openssh-client openssh-server bash libsecret-1-0 openjdk-17-jdk maven && \\\n    apt-get purge -y wget && \\\n    apt-get clean\n\nENV HOME=/home/theia\nWORKDIR /home/theia\n\n# Copy application from builder-stage\nCOPY --from=build-stage --chown=theia:theia /home/theia /home/theia\n\nEXPOSE 3000\n\n# Specify default shell for Theia and the Built-In plugins directory\nENV SHELL=/bin/bash \\\n    THEIA_DEFAULT_PLUGINS=local-dir:/home/theia/plugins\n\n# Use installed git instead of dugite\nENV USE_LOCAL_GIT=true\n\n# Switch to Theia user\nUSER theia\nWORKDIR /home/theia/applications/browser\n\n# Launch the backend application via node\nENTRYPOINT [ \"node\", \"/home/theia/applications/browser/lib/backend/main.js\" ]\n\n# Arguments passed to the application\nCMD [ \"/home/project\", \"--hostname=0.0.0.0\" ]\n"
  },
  {
    "path": "cleanup/Jenkinsfile",
    "content": "pipeline {\n    agent {\n        label 'windows'\n    }\n    triggers { cron('@weekly') }\n    options {\n        timeout(time: 1, unit: 'HOURS')\n        disableConcurrentBuilds()\n    }\n    stages {\n        stage('Cleanup Windows temp directory') {\n            steps {\n                script {\n                    listTemp('before cleanup', '%temp%')\n                    cleanFiles('tmp/plugin-download', '%temp%', 'theia-plugin-download**')\n                    cleanDirs('tmp/yarn', '%temp%', 'yarn--**')\n                    cleanDirs('tmp/lighthouse', '%temp%', 'lighthouse.*')\n                    listTemp('after cleanup', '%temp%')\n                    cleanDir('appdata/local/electron', '\\\"%LocalAppData%\\\"\\\\electron\\\\Cache')\n                    cleanYarnCache('appdata/local/yarn')\n                }\n            }\n        }\n    }\n}\n\nObject listTemp(String label, String temp) {\n    echo \"in listTemp():  ${label}\"\n    bat \"DIR ${temp}\"\n    return\n}\n\nObject cleanFiles(String label, String parent, String pattern) {\n    echo \"in cleanFile() - clean: ${label} files\"\n    echo \"parent: ${parent}, pattern: ${pattern}\"\n\n    // use \"returnStatus\" option to avoid an exception being thrown if no\n    // matching files are found, failing the pipeline\n    s = bat(\n            script: \"FORFILES /p ${parent} /m ${pattern} /C \\\"cmd /c Del /q @file\\\"\",\n            returnStatus: true\n        )\n    if (s != 0) {\n        echo \"No ${pattern} file found... Good I guess\"\n    }\n}\n\nObject cleanDirs(String label, String parent, String pattern) {\n    echo \"Before ${label} Cleanup:\"\n\n    bat \"FOR /D /R ${parent} %%i in (${pattern}) do echo \\\"%%i\\\"\"\n    bat \"FOR /D /R ${parent} %%i in (${pattern}) do @rmdir /s /q \\\"%%i\\\"\"\n\n    echo \"After ${label} Cleanup:\"\n    bat \"FOR /D /R ${parent} %%i in (${pattern}) do echo \\\"%%i\\\"\"\n    return\n}\n\nObject cleanDir(String label, String parent) {\n    echo \"Before ${label} Cleanup:\"\n\n    bat \"FOR /D /R ${parent} %%i in (*) do echo \\\"%%i\\\"\"\n    bat \"if exist ${parent} @rmdir /s /q ${parent}\"\n\n    echo \"After ${label} Cleanup:\"\n    bat \"FOR /D /R ${parent} %%i in (*) do echo \\\"%%i\\\"\"\n    return\n}\n\nObject cleanYarnCache(String label) {\n    echo \"Cleaning-up: ${label}\"\n    sh 'yarn cache clean --all'\n    return\n}\n\n"
  },
  {
    "path": "configs/base.eslintrc.json",
    "content": "{\n  \"parser\": \"@typescript-eslint/parser\",\n  \"parserOptions\": {\n    \"sourceType\": \"module\",\n    \"ecmaVersion\": 6,\n    \"ecmaFeatures\": {\n      \"jsx\": true\n    }\n  },\n  \"plugins\": [\n    \"@typescript-eslint\",\n    \"@typescript-eslint/tslint\",\n    \"import\",\n    \"no-null\"\n  ],\n  \"env\": {\n    \"browser\": true,\n    \"mocha\": true,\n    \"node\": true\n  },\n  \"ignorePatterns\": [\n    \"node_modules\",\n    \"lib\"\n  ]\n}\n"
  },
  {
    "path": "configs/base.tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"skipLibCheck\": true,\n        \"declaration\": true,\n        \"declarationMap\": true,\n        \"noImplicitAny\": true,\n        \"noEmitOnError\": false,\n        \"noImplicitThis\": true,\n        \"noUnusedLocals\": true,\n        \"strictNullChecks\": true,\n        \"experimentalDecorators\": true,\n        \"emitDecoratorMetadata\": true,\n        \"downlevelIteration\": true,\n        \"resolveJsonModule\": true,\n        \"module\": \"commonjs\",\n        \"moduleResolution\": \"node\",\n        \"target\": \"ES2017\",\n        \"jsx\": \"react\",\n        \"lib\": [\n            \"ES2017\",\n            \"ES2020.Promise\",\n            \"dom\"\n        ],\n        \"sourceMap\": true,\n        \"composite\": true\n    }\n}\n"
  },
  {
    "path": "configs/build.eslintrc.json",
    "content": "{\n  \"extends\": [\n    \"./base.eslintrc.json\",\n    \"./errors.eslintrc.json\"\n  ]\n}\n"
  },
  {
    "path": "configs/errors.eslintrc.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/eslintrc\",\n  \"rules\": {\n    \"@typescript-eslint/consistent-type-definitions\": \"error\",\n    \"@typescript-eslint/indent\": \"off\",\n    \"@typescript-eslint/no-explicit-any\": \"error\",\n    \"@typescript-eslint/quotes\": [\n      \"error\",\n      \"single\",\n      {\n        \"avoidEscape\": true\n      }\n    ],\n    \"@typescript-eslint/semi\": [\n      \"error\",\n      \"always\"\n    ],\n    \"@typescript-eslint/type-annotation-spacing\": \"error\",\n    \"arrow-body-style\": [\n      \"error\",\n      \"as-needed\"\n    ],\n    \"arrow-parens\": [\n      \"error\",\n      \"as-needed\"\n    ],\n    \"camelcase\": \"off\",\n    \"comma-dangle\": \"off\",\n    \"curly\": \"error\",\n    \"eol-last\": \"error\",\n    \"eqeqeq\": [\n      \"error\",\n      \"smart\"\n    ],\n    \"guard-for-in\": \"error\",\n    \"id-blacklist\": \"off\",\n    \"id-match\": \"off\",\n    \"max-len\": [\n      \"error\",\n      {\n        \"code\": 180\n      }\n    ],\n    \"no-magic-numbers\": \"off\",\n    \"no-multiple-empty-lines\": [\n      \"error\",\n      {\n        \"max\": 1\n      }\n    ],\n    \"no-new-wrappers\": \"error\",\n    \"no-null/no-null\": \"error\",\n    \"no-shadow\": \"off\",\n    \"@typescript-eslint/no-shadow\": [\n      \"error\",\n      {\n        \"hoist\": \"all\"\n      }\n    ],\n    \"no-tabs\": \"error\",\n    \"no-throw-literal\": \"error\",\n    \"no-trailing-spaces\": \"error\",\n    \"no-underscore-dangle\": \"off\",\n    \"no-unused-expressions\": \"error\",\n    \"no-var\": \"error\",\n    \"no-void\": \"error\",\n    \"one-var\": [\n      \"error\",\n      \"never\"\n    ],\n    \"prefer-const\": [\n      \"error\",\n      {\n        \"destructuring\": \"all\"\n      }\n    ],\n    \"radix\": \"off\",\n    \"space-before-function-paren\": [\n      \"error\",\n      {\n        \"anonymous\": \"always\",\n        \"named\": \"never\",\n        \"asyncArrow\": \"always\"\n      }\n    ],\n    \"spaced-comment\": [\n      \"error\",\n      \"always\",\n      {\n        \"exceptions\": [\n          \"*\",\n          \"+\",\n          \"-\",\n          \"/\"\n        ]\n      }\n    ],\n    \"@typescript-eslint/tslint/config\": [\n      \"error\",\n      {\n        \"rules\": {\n          \"file-header\": [\n            true,\n            \"SPDX-License-Identifier: MIT\"\n          ],\n          \"jsdoc-format\": [\n            true,\n            \"check-multiline-start\"\n          ],\n          \"one-line\": [\n            true,\n            \"check-open-brace\",\n            \"check-catch\",\n            \"check-else\",\n            \"check-whitespace\"\n          ],\n          \"typedef\": [\n            true,\n            \"call-signature\",\n            \"property-declaration\"\n          ],\n          \"whitespace\": [\n            true,\n            \"check-branch\",\n            \"check-decl\",\n            \"check-operator\",\n            \"check-separator\",\n            \"check-type\"\n          ]\n        }\n      }\n    ],\n    \"import/no-extraneous-dependencies\": \"error\"\n  },\n  \"overrides\": [\n    {\n      \"files\": [\n        \"dev-packages\",\n        \"*.{spec,espec,slow-spec}.{js,ts}\"\n      ],\n      \"rules\": {\n        \"import/no-extraneous-dependencies\": \"off\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "configs/license-check-config.json",
    "content": "{\n    \"project\": \"ecd.theia\",\n    \"inputFile\": \"yarn.lock\",\n    \"batch\": 50,\n    \"timeout\": 240,\n    \"summary\": \"license-check-summary.txt\"\n}"
  },
  {
    "path": "configs/tsconfig.eslint.json",
    "content": "{\n    \"extends\": \"../tsconfig.json\",\n    \"compilerOptions\": {\n        \"noEmit\": true\n    },\n    \"include\": [\n      \"../scripts\"\n    ]\n  }\n  "
  },
  {
    "path": "configs/warnings.eslintrc.json",
    "content": "{\n  \"plugins\": [\n    \"deprecation\"\n  ],\n  \"rules\": {\n    \"@typescript-eslint/await-thenable\": \"warn\",\n    \"no-return-await\": \"warn\",\n    \"deprecation/deprecation\": \"warn\"\n  }\n}"
  },
  {
    "path": "configs/xss.eslintrc.json",
    "content": "{\n  \"extends\": [\"plugin:no-unsanitized/DOM\"],\n  \"plugins\": [\"no-unsanitized\", \"react\"],\n  \"parserOptions\": {\n    \"ecmaFeatures\": {\n      \"jsx\": true\n    }\n  },\n  \"rules\": {\n    \"no-unsanitized/method\": [\n      \"warn\", {\n        \"escape\": {\n          \"methods\": [\"DOMPurify.sanitize\"]\n        }\n      }\n    ],\n    \"no-unsanitized/property\": [\n      \"warn\", {\n        \"escape\": {\n          \"methods\": [\"DOMPurify.sanitize\"]\n        }\n      }\n    ],\n    \"no-eval\": \"warn\",\n    \"no-implied-eval\": \"warn\",\n    \"react/no-danger-with-children\": \"warn\",\n    \"react/no-danger\": \"warn\"\n  }\n}\n"
  },
  {
    "path": "docs/developing-with-local-theia.md",
    "content": "# Developing with a Local Theia Framework\n\nThis guide explains how to build and test the Theia IDE against a local development version of the [Theia framework](https://github.com/eclipse-theia/theia). This is useful when you need to:\n\n- Test Theia IDE changes against unreleased Theia features\n- Debug issues that span both the framework and the Theia IDE\n- Develop new Theia features and immediately test them in the Theia IDE\n\n## Prerequisites\n\n- Node.js and npm installed (see [Theia prerequisites](https://github.com/eclipse-theia/theia/blob/master/doc/Developing.md#prerequisites))\n- A local clone of the [Theia repository](https://github.com/eclipse-theia/theia)\n\nThe recommended setup is to have both repositories cloned as siblings:\n\n```text\nparent-directory/\n  theia/          # Theia framework\n  theia-ide/      # This repository\n```\n\nThis matches the script's default `--theia-path` of `../theia`. You can clone Theia anywhere and specify the path with `--theia-path`.\n\n## Important Note\n\nThis script does not update the IDE version or Theia package versions in `package.json` files. It uses the current state of both repositories and relies on yarn linking to override the dependencies. If needed, you can run versioning commands (e.g., `yarn update:theia <version>`) separately before building.\n\n## Quick Start\n\n```sh\n# Clone Theia as a sibling (if not already done)\ngit clone https://github.com/eclipse-theia/theia.git ../theia\n\n# Build everything\nnode scripts/build-with-local-theia.js\n```\n\n## What the Script Does\n\n1. Build the local Theia framework (`npm ci` + `npm run compile`)\n2. Create yarn links for all `@theia/*` packages\n3. Link those packages into the Theia IDE\n4. Build the Theia IDE extensions and electron-next application\n5. Download required plugins\n\n## Usage\n\n### Full Build\n\n```sh\nnode scripts/build-with-local-theia.js\n```\n\n### Using a Different Theia Location\n\n```sh\nnode scripts/build-with-local-theia.js --theia-path /path/to/theia\n```\n\n### Incremental Development\n\nAfter the initial build, you can iterate faster:\n\n```sh\n# Rebuild only Theia IDE (Theia unchanged)\nnode scripts/build-with-local-theia.js --skip-theia-build\n\n# Rebuild only Theia packages, then rebuild Theia IDE\ncd ../theia && npm run compile\ncd ../theia-ide && yarn build:applications:next:dev\n```\n\n### Build and Package\n\nTo create a distributable application:\n\n```sh\nnode scripts/build-with-local-theia.js --package\n```\n\nThe packaged application will be in `applications/electron-next/dist/`.\n\n### Set Up Links Only\n\nIf you want to manage builds manually:\n\n```sh\nnode scripts/build-with-local-theia.js --skip-theia-build --skip-ide-build\n```\n\n### Skip Plugin Download\n\nIf you already have plugins or want to skip downloading them:\n\n```sh\nnode scripts/build-with-local-theia.js --skip-plugins\n```\n\n### Restore Normal Dependencies\n\nWhen you're done testing with the local Theia:\n\n```sh\nnode scripts/build-with-local-theia.js --unlink\n```\n\nThis removes all yarn links and reinstalls packages from npm.\n\n### Dry Run\n\nPreview what commands will be executed:\n\n```sh\nnode scripts/build-with-local-theia.js --dry-run\n```\n\n## Running the Theia IDE (Next)\n\nAfter building:\n\n```sh\nyarn --cwd applications/electron-next start\n```\n\nIf you packaged the application with `--package`, you can also run the packaged version directly from `applications/electron-next/dist/`.\n\n## Development Workflow\n\nA typical development workflow looks like:\n\n1. **Initial setup**: Clone Theia and run the full build\n\n    ```sh\n    git clone https://github.com/eclipse-theia/theia.git ../theia\n    node scripts/build-with-local-theia.js\n    ```\n\n2. **Make changes in Theia**: Edit files in `../theia`\n\n3. **Rebuild Theia**:\n\n    ```sh\n    cd ../theia && npm run compile\n    ```\n\n4. **Rebuild and run Theia IDE**:\n\n    ```sh\n    cd ../theia-ide\n    yarn build:applications:next:dev\n    yarn --cwd applications/electron-next start\n    ```\n\n5. **When done**: Restore npm dependencies\n\n    ```sh\n    node scripts/build-with-local-theia.js --unlink\n    ```\n\n## Command Reference\n\n| Option                | Description                                          |\n|-----------------------|------------------------------------------------------|\n| `--theia-path <path>` | Path to local Theia repository (default: `../theia`) |\n| `--skip-theia-build`  | Skip building Theia packages (use if already built)  |\n| `--skip-ide-build`    | Skip building Theia IDE (use for linking only)       |\n| `--skip-plugins`      | Skip downloading plugins                             |\n| `--package`           | Package the electron-next application after building |\n| `--unlink`            | Remove links and restore npm dependencies            |\n| `--dry-run`           | Print commands without executing them                |\n| `--help`              | Show help message                                    |\n\n## Troubleshooting\n\n### \"Theia directory not found\"\n\nMake sure you have cloned the Theia repository:\n\n```sh\ngit clone https://github.com/eclipse-theia/theia.git ../theia\n```\n\nOr specify the correct path:\n\n```sh\nnode scripts/build-with-local-theia.js --theia-path /correct/path/to/theia\n```\n\n### \"Package not found in local Theia\"\n\nSome `@theia/*` packages used by the Theia IDE may not exist in your Theia checkout. This can happen if:\n\n- You're on an older Theia branch that doesn't have newer packages\n- The package is from a different source\n\nThe script will warn about missing packages but continue with available ones.\n\n### Build Errors After Switching Branches\n\nIf you switch branches in either repository, clean and rebuild:\n\n```sh\n# In theia-ide\ngit clean -xfd\nnode scripts/build-with-local-theia.js\n\n# Or if only Theia packages changed\ncd ../theia && git clean -xfd && npm ci && npm run compile\ncd ../theia-ide && yarn build:applications:next:dev\n```\n\n### Restoring Clean State\n\nIf things get into a bad state:\n\n```sh\n# Unlink and restore npm packages\nnode scripts/build-with-local-theia.js --unlink\n\n# Full clean rebuild\ngit clean -xfd\nyarn && yarn build:dev && yarn download:plugins\n```\n"
  },
  {
    "path": "lerna.json",
    "content": "{\n  \"$schema\": \"node_modules/lerna/schemas/lerna-schema.json\",\n  \"version\": \"1.71.100\",\n  \"command\": {\n    \"run\": {\n      \"stream\": true\n    }\n  }\n}"
  },
  {
    "path": "next/Jenkinsfile",
    "content": "/**\n * This Jenkinsfile builds Theia Next across the major OS platforms\n */\nimport groovy.json.JsonSlurper\n\ndistFolder = \"applications/electron/dist\"\n\npipeline {\n    agent none\n    triggers { cron('@daily') }\n    options {\n        timeout(time: 5, unit: 'HOURS')\n        disableConcurrentBuilds()\n    }\n    environment {\n        NODE_OPTIONS = '--max_old_space_size=4096'\n    }\n    stages {\n        stage('Build') {\n            parallel {\n                stage('Test Linux Theia@Next') {\n                    agent {\n                        kubernetes {\n                            yaml \"\"\"\napiVersion: v1\nkind: Pod\nspec:\n  containers:\n  - name: theia-dev\n    image: eclipsetheia/theia-blueprint:builder\n    imagePullPolicy: Always\n    command:\n    - cat\n    tty: true\n    resources:\n      limits:\n        memory: \"8Gi\"\n        cpu: \"2\"\n      requests:\n        memory: \"8Gi\"\n        cpu: \"2\"\n    volumeMounts:\n    - name: global-cache\n      mountPath: /.cache\n    - name: global-yarn\n      mountPath: /.yarn      \n    - name: global-npm\n      mountPath: /.npm      \n    - name: electron-cache\n      mountPath: /.electron-gyp\n  volumes:\n  - name: global-cache\n    emptyDir: {}\n  - name: global-yarn\n    emptyDir: {}\n  - name: global-npm\n    emptyDir: {}\n  - name: electron-cache\n    emptyDir: {}\n\"\"\"\n                        }\n                    }\n                    steps {\n                        container('theia-dev') {\n                            withCredentials([string(credentialsId: \"github-bot-token\", variable: 'GITHUB_TOKEN')]) {\n                                script {\n                                    buildNext(false, 1200)\n                                }\n                            }\n                        }\n                    }\n                    post {\n                        failure {\n                            error(\"Linux installer creation failed, aborting...\")\n                        }\n                    }\n                }\n                stage('Test Mac Theia@Next') {\n                    agent {\n                        label 'macos'\n                    }\n                    steps {\n                        script {\n                            buildNext(false, 60)\n                        }\n                    }\n                    post {\n                        failure {\n                            error(\"Mac installer creation failed, aborting...\")\n                        }\n                    }\n                }\n                stage('Test Windows Theia@Next') {\n                    agent {\n                        label 'windows'\n                    }\n                    steps {\n                        script {\n                            sh \"npm config set msvs_version 2017\"\n                            sh \"npx node-gyp install 14.20.0\"\n                            buildNext(true, 60)\n                        }\n                    }\n                    post {\n                        failure {\n                            error(\"Windows installer creation failed, aborting...\")\n                        }\n                    }\n                }\n            }\n        }\n    }\n    post {\n        failure {\n            echo \"Build result FAILURE: Send email notification to jfaltermeier@eclipsesource.com\"\n            emailext attachLog: true,\n            body: 'Job: ${JOB_NAME}<br>Build Number: ${BUILD_NUMBER}<br>Build URL: ${BUILD_URL}',\n            mimeType: 'text/html', subject: 'Build ${JOB_NAME} (#${BUILD_NUMBER}) FAILURE', to: 'jfaltermeier@eclipsesource.com'\n        }\n        unstable {\n            echo \"Build result UNSTABLE: Send email notification to jfaltermeier@eclipsesource.com\"\n            emailext attachLog: true,\n            body: 'Job: ${JOB_NAME}<br>Build Number: ${BUILD_NUMBER}<br>Build URL: ${BUILD_URL}',\n            mimeType: 'text/html', subject: 'Build ${JOB_NAME} (#${BUILD_NUMBER}) UNSTABLE', to: 'jfaltermeier@eclipsesource.com'\n        }\n    }\n}\n\ndef buildNext(boolean runTests, int sleepBetweenRetries) {\n    int MAX_RETRY = 3\n\n    checkout scm\n\n    // merge next branch into master to get any known fixes for next version\n    // TODO there might be a more elegant way to merge a branch into this one \n    // using a jenkings plugin from here\n    sh \"git config user.email \\\"not@real.user\\\"\"\n    sh \"git config user.name \\\"Not a real user\\\"\"\n    sh \"git fetch origin next\"\n    sh \"git merge FETCH_HEAD\"\n    sh \"node --version\"\n\n    // regular build\n    sh \"printenv && yarn cache dir\"\n\n    sh \"yarn cache clean\"\n    try {\n        sh(script: 'yarn --frozen-lockfile --force')\n    } catch(error) {\n        retry(MAX_RETRY) {\n            sleep(sleepBetweenRetries)\n            echo \"yarn failed - Retrying\"\n            sh(script: 'yarn --frozen-lockfile --force')        \n        }\n    }\n\n    echo \"Updating theia versions to next\"\n    sh \"yarn update:next\"\n    try {\n        sh(script: 'yarn --force')\n    } catch(error) {\n        retry(MAX_RETRY) {\n            sleep(sleepBetweenRetries)\n            echo \"yarn failed - Retrying\"\n            sh(script: 'yarn --force')        \n        }\n    }\n\n    echo \"Upgrading versions\"\n    sh \"yarn upgrade\"\n    sh \"git clean -xfd\"\n    try {\n        sh(script: 'yarn --force')\n    } catch(error) {\n        retry(MAX_RETRY) {\n            sleep(sleepBetweenRetries)\n            echo \"yarn failed - Retrying\"\n            sh(script: 'yarn --force')        \n        }\n    }\n\n    sh \"rm -rf ./${distFolder}\"\n    sh \"yarn build\"\n    sh \"yarn download:plugins\"\n    sshagent(['projects-storage.eclipse.org-bot-ssh']) {\n        sh \"yarn electron package:preview\"\n    }\n    if (runTests) {\n        wrap([$class: 'Xvnc', takeScreenshot: false, useXauthority: true]) {\n            sh 'yarn electron test'\n        }\n    } \n}\n"
  },
  {
    "path": "next/NEXT_INTEGRATION_BUILD.md",
    "content": "# Integration build against Theia@next\n\nThe master branch has a script that may be used to update all theia versions to `next`. This may be executed running `yarn update:next` in the repository root.\nWe will set up an integration job that will build the Eclipse Theia IDE against Theia@next so that we may identify breaking changes early.\n\n## Build process\n\n* Check out the master branch containing all of the latest changes.\n* Merge the next branch into the checked out branch. The next branch will only contain fixes that are required to build against the `next` version (e.g. a method was renamed and causes compile errors)\n* Run `yarn update:next` (may have been exectued on `next` already but not necessarily)\n* Build the IDE and run the tests\n* In case of an error notify responsible persons via e-mail\n\nThis process should make sure that we always include all of the latest changes (by using the master branch as a starting point for the build) and only have to maintain fixes (on the `next` branch) in case of actual problems.\n\n## In case of errors\n\n* Analyse the issue and open a bug report\n* Either fix the issue (open a PR and merge to `next` branch) or create some kind of hotfix for the `next` branch to get a green build.\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"private\": true,\n  \"version\": \"1.71.100\",\n  \"license\": \"MIT\",\n  \"author\": \"Rob Moran <github@thegecko.org>\",\n  \"homepage\": \"https://github.com/eclipse-theia/theia-ide#readme\",\n  \"bugs\": {\n    \"url\": \"https://github.com/eclipse-theia/theia/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/eclipse-theia/theia-ide.git\"\n  },\n  \"engines\": {\n    \"yarn\": \">=1.7.0 <2\",\n    \"node\": \">=22\"\n  },\n  \"devDependencies\": {\n    \"@eclipse-dash/nodejs-wrapper\": \"^0.0.1\",\n    \"@theia/cli\": \"1.72.0-next.20\",\n    \"@types/yargs\": \"17.0.7\",\n    \"@typescript-eslint/eslint-plugin\": \"^4.33.0\",\n    \"@typescript-eslint/eslint-plugin-tslint\": \"^4.33.0\",\n    \"@typescript-eslint/parser\": \"^4.33.0\",\n    \"eslint\": \"^7.32.0\",\n    \"eslint-plugin-deprecation\": \"1.2.1\",\n    \"eslint-plugin-import\": \"^2.32.0\",\n    \"eslint-plugin-no-null\": \"^1.0.2\",\n    \"eslint-plugin-no-unsanitized\": \"^3.2.0\",\n    \"eslint-plugin-react\": \"^7.37.5\",\n    \"lerna\": \"^9.0.7\",\n    \"node-gyp\": \"^11.5.0\",\n    \"rimraf\": \"^2.7.1\",\n    \"ts-node\": \"^10.9.2\",\n    \"type-fest\": \"^0.20.2\",\n    \"yargs\": \"17.2.1\"\n  },\n  \"scripts\": {\n    \"clean\": \"lerna run clean && rimraf node_modules\",\n    \"build\": \"yarn build:extensions && yarn build:applications\",\n    \"build:dev\": \"yarn build:extensions && yarn build:applications:dev\",\n    \"build:applications\": \"lerna run --scope=\\\"theia-ide*app\\\" --ignore=\\\"theia-ide-next*\\\" build:prod --concurrency 1\",\n    \"build:applications:dev\": \"lerna run --scope=\\\"theia-ide*app\\\" --ignore=\\\"theia-ide-next*\\\" build --concurrency 1\",\n    \"build:applications:next\": \"lerna run --scope=\\\"theia-ide-next*\\\" build:prod --concurrency 1\",\n    \"build:applications:next:dev\": \"lerna run --scope=\\\"theia-ide-next*\\\" build --concurrency 1\",\n    \"build:extensions\": \"lerna run --scope=\\\"theia-ide*ext\\\" build\",\n    \"download:plugins\": \"theia download:plugins --rate-limit=15 --parallel=false && yarn permissions:writeable\",\n    \"package:applications\": \"lerna run --scope=\\\"theia-ide*app\\\" --ignore=\\\"theia-ide-next*\\\" package --concurrency 1\",\n    \"package:applications:preview\": \"lerna run --scope=\\\"theia-ide*app\\\" --ignore=\\\"theia-ide-next*\\\" package:preview --concurrency 1\",\n    \"package:applications:prod\": \"lerna run --scope=\\\"theia-ide*app\\\" --ignore=\\\"theia-ide-next*\\\" package:prod --concurrency 1\",\n    \"package:applications:next\": \"lerna run --scope=\\\"theia-ide-next*\\\" package --concurrency 1\",\n    \"permissions:writeable\": \"ts-node scripts/make-files-writeable.ts plugins\",\n    \"watch\": \"lerna run --parallel watch\",\n    \"test\": \"lerna run test\",\n    \"electron\": \"yarn --cwd applications/electron\",\n    \"browser\": \"yarn --cwd applications/browser\",\n    \"update:theia\": \"ts-node scripts/update-theia-version.ts\",\n    \"update:theia:children\": \"lerna run update:theia -- \",\n    \"update:next\": \"ts-node scripts/update-theia-version.ts next && lerna run update:next\",\n    \"lint\": \"eslint --ext js,jsx,ts,tsx scripts && lerna run lint\",\n    \"lint:fix\": \"eslint --ext js,jsx,ts,tsx scripts --fix && lerna run lint:fix\",\n    \"license:check\": \"npx dash-licenses-wrapper  --configFile=./configs/license-check-config.json\",\n    \"license:check:review\": \"npx dash-licenses-wrapper  --configFile=./configs/license-check-config.json --review\",\n    \"postinstall\": \"theia-patch && npx patch-package --patch-dir patches\"\n  },\n  \"theiaPluginsDir\": \"plugins\",\n  \"theiaPlugins\": {\n    \"vscode-builtin-extensions\": \"https://github.com/eclipse-theia/vscode-builtin-extensions/releases/download/1.108.2/vscode-builtin-extensions-1.108.2.tar.gz\",\n    \"vscjava.vscode-java-pack\": \"https://open-vsx.org/api/vscjava/vscode-java-pack/0.30.5/file/vscjava.vscode-java-pack-0.30.5.vsix\",\n    \"vscjava.vscode-java-dependency\": \"https://open-vsx.org/api/vscjava/vscode-java-dependency/0.27.0/file/vscjava.vscode-java-dependency-0.27.0.vsix\"\n  },\n  \"theiaPluginsExcludeIds\": [\n    \"ms-vscode.js-debug-companion\",\n    \"VisualStudioExptTeam.vscodeintellicode\",\n    \"vscode.extension-editing\",\n    \"vscode.github\",\n    \"vscode.github-authentication\",\n    \"vscode.microsoft-authentication\"\n  ],\n  \"workspaces\": [\n    \"applications/*\",\n    \"theia-extensions/*\"\n  ],\n  \"resolutions\": {\n    \"@types/puppeteer\": \"^5.4.7\",\n    \"@yarnpkg/parsers\": \"3.0.0-rc.44\",\n    \"**/multer\": \"1.4.4-lts.1\",\n    \"**/nan\": \"2.25.0\",\n    \"**/cpu-features\": \"0.0.9\",\n    \"**/perfect-scrollbar\": \"1.5.5\",\n    \"**/node-abi\": \"4.14.0\"\n  }\n}\n"
  },
  {
    "path": "patches/@theia+terminal+1.72.0-next.20.patch",
    "content": "diff --git a/node_modules/@theia/terminal/lib/node/shell-integration-injector.js b/node_modules/@theia/terminal/lib/node/shell-integration-injector.js\nindex 1234567..abcdefg 100644\n--- a/node_modules/@theia/terminal/lib/node/shell-integration-injector.js\n+++ b/node_modules/@theia/terminal/lib/node/shell-integration-injector.js\n@@ -77,7 +77,7 @@ let ShellIntegrationInjector = class ShellIntegrationInjector {\n             console.warn(`Shell integration file not found (application may not be bundled correctly): ${fullPath}`);\n             return undefined;\n         }\n-        return fullPath;\n+        return fullPath.replace('.asar' + path.sep, '.asar.unpacked' + path.sep);\n     }\n     stripLoginFlag(args) {\n         if (args === undefined) {\n"
  },
  {
    "path": "releng/preview/Jenkinsfile.build",
    "content": "/**\n * This Jenkinsfile builds Theia across the major OS platforms\n * It's designed to be run only manually\n */\n\nimport groovy.json.JsonSlurper\n\n// Set permissions to allow the sign job to copy artifacts from this build\nproperties([\n    copyArtifactPermission('theia-ide-sign-notarize'),\n    copyArtifactPermission('theia-ide-upload')\n])\n\npipeline {\n    agent none\n    \n    // Store important values for later use\n    environment {\n        STORED_GIT_BRANCH = \"${env.GIT_BRANCH ?: env.BRANCH_NAME ?: 'unknown'}\"\n        THEIA_IDE_JENKINS_CI = 'true'\n        msvs_version = '2022'\n        GYP_MSVS_VERSION = '2022'\n        npm_config_msvs_version = '2022'\n        NODE_OPTIONS = '--max_old_space_size=4096'\n    }\n    options {\n        timeout(time: 5, unit: 'HOURS')\n        disableConcurrentBuilds()\n        durabilityHint('MAX_SURVIVABILITY')\n    }\n    parameters {\n        booleanParam(name: 'DRY_RUN', defaultValue: true, description: 'If true, treat this as a dry run (no deployment of artifacts)')\n        booleanParam(name: 'IS_PR', defaultValue: true, description: 'If true, treat this as a PR (only run this stage)')\n    }\n    stages {\n        stage('Build') {\n            parallel {\n                stage('Linux: Create Installer') {\n                    agent {\n                        kubernetes {\n                            yaml \"\"\"\napiVersion: v1\nkind: Pod\nspec:\n  podRetention: never()\n  containers:\n  - name: theia-dev\n    image: eclipsetheia/theia-blueprint:builder\n    imagePullPolicy: Always\n    command:\n    - cat\n    tty: true\n    resources:\n      limits:\n        memory: \"8000Mi\"\n        cpu: \"2000m\"\n      requests:\n        memory: \"512Mi\"\n        cpu: \"200m\"\n    volumeMounts:\n    - name: global-cache\n      mountPath: /.cache\n    - name: global-yarn\n      mountPath: /.yarn      \n    - name: global-npm\n      mountPath: /.npm      \n    - name: electron-cache\n      mountPath: /.electron-gyp\n  volumes:\n  - name: global-cache\n    emptyDir: {}\n  - name: global-yarn\n    emptyDir: {}\n  - name: global-npm\n    emptyDir: {}\n  - name: electron-cache\n    emptyDir: {}\n\"\"\"\n                        }\n                    }\n                    steps {\n                        container('theia-dev') {\n                            withCredentials([string(credentialsId: \"github-bot-token\", variable: 'GITHUB_TOKEN')]) {\n                                script {\n                                    buildInstaller(120)\n                                }\n                            }\n                        }\n                        // Move Linux artifacts to linux folder for archiving\n                        sh \"mkdir -p applications/electron/dist/linux\"\n                        sh \"find applications/electron/dist -maxdepth 1 -type f -not -path '*/\\\\.*' -exec mv {} applications/electron/dist/linux/ \\\\;\"\n                        archiveArtifacts artifacts: \"applications/electron/dist/linux/*\", fingerprint: true\n                    }\n                    post {\n                        failure {\n                            error(\"Linux installer creation failed, aborting...\")\n                        }\n                    }\n                }\n                stage('Mac: Create Installer') {\n                    options {\n                        skipDefaultCheckout true\n                    }\n                    agent {\n                        label 'macos'\n                    }\n                    steps {\n                        nodejs(nodeJSInstallationName: 'node_22.x') {\n                            script {\n                                sh \"node --version\"\n                                createMacInstaller()\n                            }\n                        }\n                        archiveArtifacts artifacts: \"applications/electron/dist/**\", fingerprint: true\n                    }\n                    post {\n                        failure {\n                            error(\"Mac installer creation failed, aborting...\")\n                        }\n                    }\n                }\n                stage('Windows: Create Installer') {\n                    // Windows installer creation is split into two sub-stages running on\n                    // different agents:\n                    //   1. Build the unpacked electron app on a real Windows agent.\n                    //   2. Sign each internal `.exe` via the Eclipse Authenticode service and\n                    //      package the NSIS installer from the already-signed directory on a\n                    //      Linux k8s pod\n                    stages {\n                        stage('Windows: Build unpacked') {\n                            agent {\n                                label 'windows'\n                            }\n                            steps {\n                                nodejs(nodeJSInstallationName: 'node_24.x') {\n                                    // analyze memory usage\n                                    bat \"wmic ComputerSystem get TotalPhysicalMemory\"\n                                    bat \"wmic OS get FreePhysicalMemory\"\n                                    bat \"tasklist\"\n\n                                    buildUnpackedWindows()\n                                }\n                                retry(3) {\n                                    timeout(time: 30, unit: 'MINUTES') {\n                                        stash includes: 'applications/electron/dist/win-unpacked/**',\n                                              name: 'windows-unpacked'\n                                    }\n                                }\n                            }\n                        }\n                        stage('Windows: Sign & Package') {\n                            agent {\n                                kubernetes {\n                                    yaml \"\"\"\napiVersion: v1\nkind: Pod\nspec:\n  podRetention: never()\n  containers:\n  - name: theia-dev\n    image: eclipsetheia/theia-blueprint:builder\n    imagePullPolicy: Always\n    command:\n    - cat\n    tty: true\n    resources:\n      limits:\n        memory: \"4000Mi\"\n        cpu: \"1000m\"\n      requests:\n        memory: \"512Mi\"\n        cpu: \"200m\"\n    volumeMounts:\n    - name: global-cache\n      mountPath: /.cache\n    - name: global-yarn\n      mountPath: /.yarn\n    - name: global-npm\n      mountPath: /.npm\n    - name: electron-cache\n      mountPath: /.electron-gyp\n  volumes:\n  - name: global-cache\n    emptyDir: {}\n  - name: global-yarn\n    emptyDir: {}\n  - name: global-npm\n    emptyDir: {}\n  - name: electron-cache\n    emptyDir: {}\n\"\"\"\n                                }\n                            }\n                            environment {\n                                WINEPREFIX = \"${env.WORKSPACE}/.wine-prefix\"\n                            }\n                            steps {\n                                checkout scm\n                                retry(3) {\n                                    timeout(time: 30, unit: 'MINUTES') {\n                                        unstash 'windows-unpacked'\n                                    }\n                                }\n                                container('theia-dev') {\n                                    sh '''\n                                        wine --version\n                                        wine cmd /c echo wine-prefix-ok\n                                    '''\n                                    withCredentials([string(credentialsId: \"github-bot-token\", variable: 'GITHUB_TOKEN')]) {\n                                        sh 'yarn --network-timeout 100000 --frozen-lockfile --force'\n                                        sh 'yarn --cwd applications/electron sign:directory:windows -d dist/win-unpacked'\n                                        // Generate app-update.yml for the Windows auto-updater.\n                                        // electron-builder skips this when using --dir (step 1) because the\n                                        // target is \"dir\", not \"nsis\", and --prepackaged (step 2) does not\n                                        // re-run the afterPack lifecycle.\n                                        sh 'node applications/electron/scripts/generate-app-update-yml.js'\n                                        sh 'cd applications/electron && npx electron-builder --prepackaged dist/win-unpacked --win nsis -c.win.signAndEditExecutable=false --publish always'\n                                    }\n                                }\n                                // Move Windows artifacts to windows folder for archiving\n                                sh \"mkdir -p applications/electron/dist/windows\"\n                                sh \"find applications/electron/dist -maxdepth 1 -type f -not -path '*/\\\\.*' -exec mv {} applications/electron/dist/windows/ \\\\;\"\n                                archiveArtifacts artifacts: \"applications/electron/dist/windows/*\", fingerprint: true\n                            }\n                        }\n                    }\n                    post {\n                        failure {\n                            error(\"Windows installer creation failed, aborting...\")\n                        }\n                    }\n                }\n            }\n        }\n    }\n    post {\n        success {\n            script {\n                if (isPR()) {\n                    echo \"This is a PR build. Skipping triggering of sign and notarize job.\"\n                } else {\n                    echo \"This is NOT a PR build. Preparing to trigger sign and notarize job.\"\n                    echo \"DRY_RUN parameter value: ${params.DRY_RUN}\"\n                    \n                    build job: 'theia-ide-sign-notarize', \n                            parameters: [\n                                string(name: 'BUILD_NUMBER_PARAM', value: \"${BUILD_NUMBER}\"),\n                                booleanParam(name: 'DRY_RUN', value: params.DRY_RUN)\n                            ],\n                            wait: false\n                    \n                    echo \"Triggered sign and notarize job successfully\"\n                }\n            }\n        }\n    }\n}\n\ndef detachVolume(String mountpoint) {\n    try {\n        sh \"hdiutil detach \\\"${mountpoint}\\\" -force\"\n    } catch (Exception ex) {\n        echo \"Failed to detach ${mountpoint}: ${ex}\"\n    }\n}\n\ndef createMacInstaller() {\n    // Step 0: Checkout scm\n    checkout scm\n\n    // Step 1: Ensure directory is cleaned and recreated\n    sh \"rm -rf applications/electron/dist\"\n    sh \"mkdir -p applications/electron/dist\"\n    \n    // Step 2: Download and extract zip files for both architectures\n    def architectures = ['mac-arm64', 'mac-x64']\n    architectures.each { arch ->\n        sh \"curl -L -o applications/electron/dist/${arch}.zip https://github.com/eclipse-theia/theia-ide/releases/download/pre-release/${arch}.zip\"\n        sh \"unzip -o applications/electron/dist/${arch}.zip -d applications/electron/dist/${arch}\"\n        sh \"rm applications/electron/dist/${arch}.zip\"\n    }\n    \n    sh \"ls -al applications/electron/dist/mac-arm64 applications/electron/dist/mac-x64\"\n\n    // Step 3: Unpack DMG files for signing\n    architectures.each { arch ->\n        def mountPoint = \"applications/electron/dist/${arch}/TheiaIDE-dmg-mounted\"\n        sh \"rm -rf ${mountPoint}\"\n        sh \"mkdir -p ${mountPoint}\"\n        sh \"hdiutil attach applications/electron/dist/${arch}/TheiaIDE.dmg -mountpoint ${mountPoint}\"\n        \n        // Create DMG layout structure\n        sh \"mkdir -p applications/electron/dist/${arch}/TheiaIDE-dmg-layout/.background\"\n        \n        // Copy DS_Store if exists\n        sh \"\"\"\n            if [ -f ${mountPoint}/.DS_Store ]; then\n                ditto ${mountPoint}/.DS_Store applications/electron/dist/${arch}/TheiaIDE-dmg-layout/\n            fi\n        \"\"\"\n        \n        // Copy app and create Applications symlink\n        sh \"ditto ${mountPoint}/TheiaIDE.app applications/electron/dist/${arch}/TheiaIDE-dmg-layout/TheiaIDE.app\"\n        // Make sure the destination doesn't exist before creating the symlink\n        sh \"rm -f applications/electron/dist/${arch}/TheiaIDE-dmg-layout/Applications\"\n        sh \"ln -sf /Applications applications/electron/dist/${arch}/TheiaIDE-dmg-layout/Applications\"\n        \n        // Detach mounted DMG\n        sh \"hdiutil detach ${mountPoint}\"\n        \n        // Step 4: Remove quarantine bits from all files\n        sh \"xattr -d -r com.apple.quarantine applications/electron/dist/${arch}/TheiaIDE-dmg-layout || true\"\n    }\n    \n    // Step 5: Sign binaries\n    sh 'yarn --network-timeout 100000 --frozen-lockfile --force'\n    sshagent(['projects-storage.eclipse.org-bot-ssh']) {\n        architectures.each { arch ->\n            def appPath = \"/${pwd()}/applications/electron/dist/${arch}/TheiaIDE-dmg-layout/TheiaIDE.app\"\n            sh \"yarn electron sign:directory -d \\\"${appPath}\\\"\"\n        }\n    }\n\n    // Step 6: Create the final DMG files\n    architectures.each { arch ->\n        sh \"rm -f applications/electron/dist/${arch}/TheiaIDE.dmg\"\n        sh \"hdiutil create -volname TheiaIDE -srcfolder applications/electron/dist/${arch}/TheiaIDE-dmg-layout -fs HFS+ -format UDZO applications/electron/dist/${arch}/TheiaIDE.dmg\"\n        sh \"rm -rf applications/electron/dist/${arch}/TheiaIDE-dmg-layout\"\n        \n        // Cleanup files we don't require\n        sh \"find applications/electron/dist/${arch} -type f ! -name \\\"TheiaIDE.dmg\\\" ! -name \\\"latest-mac.yml\\\" -delete\"\n    }\n    \n    sh \"ls -al applications/electron/dist/mac-arm64 applications/electron/dist/mac-x64\"\n}\n\ndef buildUnpackedWindows() {\n    checkout scm\n\n    sh \"git clean -xdf\"\n    sh \"yarn cache clean\"\n\n    sh 'node --version'\n    sh 'printenv && yarn cache dir'\n\n    sh 'yarn --network-timeout 100000 --frozen-lockfile --force'\n    sh 'yarn build:extensions'\n    sh 'yarn electron build:prod'\n    sh 'yarn download:plugins'\n    sh 'cd applications/electron && npx electron-builder --dir --win -c.mac.identity=null --publish never'\n}\n\ndef buildInstaller(int sleepBetweenRetries) {\n    int maxRetry = 1\n    String buildPackageCmd\n\n    checkout scm\n\n    // Ensure build on a clean state\n    sh \"git clean -xdf\"\n    sh \"yarn cache clean\"\n\n    sh 'node --version'\n    sh 'printenv && yarn cache dir'\n    \n    // Execute build with retry capability\n    try {\n        // run install, build extensions and build electron app in separate steps\n        sh 'yarn --network-timeout 100000 --frozen-lockfile --force'\n        sh 'yarn build:extensions'\n        sh 'yarn electron build:prod'\n    } catch (error) {\n        retry(maxRetry) {\n            sleep(sleepBetweenRetries)\n            echo 'yarn failed - Retrying'\n            sh(script: buildPackageCmd)\n        }\n    }\n\n    // Package the built application\n    sshagent(['projects-storage.eclipse.org-bot-ssh']) {\n        sh 'yarn download:plugins'\n        sh 'yarn electron package:prod'\n    }\n}\n\n// Helper function for dry run status\ndef isDryRun() {\n    return params.DRY_RUN\n}\n\n// Helper function for dPR status\ndef isPR() {\n    return params.IS_PR\n}\n"
  },
  {
    "path": "releng/preview/Jenkinsfile.sign",
    "content": "/**\n * This Jenkinsfile handles signing and notarizing Theia installers\n * It's designed to be run automatically after the build job\n */\n\nimport groovy.json.JsonSlurper\n\n// Set permissions to allow the upload job to copy artifacts from this build\nproperties([\n    copyArtifactPermission('theia-ide-upload')\n])\n\npipeline {\n    agent none\n    options {\n        timeout(time: 5, unit: 'HOURS')\n        disableConcurrentBuilds()\n        durabilityHint('MAX_SURVIVABILITY')\n    }\n    parameters {\n        string(name: 'BUILD_NUMBER_PARAM', defaultValue: '', description: 'The build number from the upstream build job')\n        booleanParam(name: 'DRY_RUN', defaultValue: true, description: 'If true, treat this as a dry run (no deployment of artifacts)')\n    }\n    environment {\n        THEIA_IDE_JENKINS_CI = 'true'\n        msvs_version = '2019'\n        GYP_MSVS_VERSION = '2019'\n        NODE_OPTIONS = '--max_old_space_size=4096'\n    }\n    stages {\n        stage('Sign and Notarize') {\n            parallel {\n                stage('Mac') {\n                    stages {\n                        stage('Mac: Sign and Notarize') {\n                            agent {\n                                kubernetes {\n                                    yaml \"\"\"\napiVersion: v1\nkind: Pod\nspec:\n  podRetention: never()\n  containers:\n  - name: theia-dev\n    image: eclipsetheia/theia-blueprint:builder\n    imagePullPolicy: Always\n    command:\n    - cat\n    tty: true\n    resources:\n      limits:\n        memory: \"4000Mi\"\n        cpu: \"1000m\"\n      requests:\n        memory: \"512Mi\"\n        cpu: \"200m\"\n    volumeMounts:\n    - name: global-cache\n      mountPath: /.cache\n    - name: global-yarn\n      mountPath: /.yarn      \n    - name: global-npm\n      mountPath: /.npm      \n    - name: electron-cache\n      mountPath: /.electron-gyp\n  - name: jnlp\n    resources:\n      limits:\n        memory: \"2000Mi\"\n        cpu: \"1000m\"\n      requests:\n        memory: \"1024Mi\"\n        cpu: \"250m\"\n  volumes:\n  - name: global-cache\n    emptyDir: {}\n  - name: global-yarn\n    emptyDir: {}\n  - name: global-npm\n    emptyDir: {}\n  - name: electron-cache\n    emptyDir: {}\n  - name: volume-known-hosts\n    configMap:\n      name: known-hosts\n\"\"\"\n                                }\n                            }\n                            steps {\n                                checkout scm\n                                // Clean and prepare target folder\n                                sh \"rm -rf applications/electron/dist\"\n                                sh \"mkdir -p applications/electron/dist\"\n\n                                echo \"Fetching artifacts from 'theia-ide-release' build #${params.BUILD_NUMBER_PARAM}\"\n\n                                // Retry copy artifacts with timeout to handle large files (up to 800 MiB)\n                                retry(3) {\n                                    timeout(time: 30, unit: 'MINUTES') {\n                                        copyArtifacts(\n                                            projectName: \"theia-ide-release\",\n                                            selector: specific(\"${params.BUILD_NUMBER_PARAM}\"),\n                                            filter: 'applications/electron/dist/**',\n                                            target: '.',\n                                            fingerprintArtifacts: true\n                                        )\n                                    }\n                                }\n                                \n                                retry(2) {\n                                    container('theia-dev') {\n                                        withCredentials([string(credentialsId: \"github-bot-token\", variable: 'GITHUB_TOKEN')]) {\n                                            script {\n                                                signInstaller('dmg', 'mac', 'mac-x64')\n                                                notarizeInstaller('dmg', 'mac-x64')\n                                                signInstaller('dmg', 'mac', 'mac-arm64')\n                                                notarizeInstaller('dmg', 'mac-arm64')\n                                            }\n                                        }\n                                    }\n                                }\n                                stash includes: \"applications/electron/dist/mac-x64/**\", name: 'mac'\n                                stash includes: \"applications/electron/dist/mac-arm64/**\", name: 'mac-arm'\n                            }\n                        }\n                        stage('Mac: Recreate Zip with Ditto for correct file permissions') {\n                            agent {\n                                label 'macos'\n                            }\n                            options {\n                                retry(2)\n                            }\n                            steps {\n                                checkout scm\n                                // Clean and prepare target folder\n                                sh \"rm -rf applications/electron/dist\"\n                                sh \"mkdir -p applications/electron/dist\"\n                                \n                                unstash 'mac'\n                                unstash 'mac-arm'\n                                script {\n                                    def packageJSON = readJSON file: \"package.json\"\n                                    String version = \"${packageJSON.version}\"\n\n                                    def architectures = ['mac-x64', 'mac-arm64']\n                                    architectures.each { arch -> \n                                        String targetFolder = \"applications/electron/dist/${arch}\"\n                                        def notarizedDmg = \"${targetFolder}/TheiaIDE.dmg\"\n                                        def mountPoint = \"${targetFolder}/TheiaIDE-mount\"\n                                        def extractedFolder = \"${targetFolder}/TheiaIDE-extracted\"\n                                        def rezippedFile = \"${targetFolder}/TheiaIDE-rezipped.zip\"\n                                        def archSuffix = arch == 'mac-arm64' ? '-arm64' : ''\n                                        def finalZip = \"${targetFolder}/TheiaIDE-${version}${archSuffix}-mac.zip\"\n                                        \n                                        // Clean and prepare\n                                        sh \"rm -rf \\\"${extractedFolder}\\\" \\\"${mountPoint}\\\"\"\n                                        sh \"mkdir -p \\\"${extractedFolder}\\\" \\\"${mountPoint}\\\"\"\n                                        \n                                        try {\n                                          // Mount DMG\n                                          sh \"hdiutil attach \\\"${notarizedDmg}\\\" -mountpoint \\\"${mountPoint}\\\"\"\n                                          sleep 5\n                                          // Copy .app and check contents\n                                          sh \"ditto \\\"${mountPoint}/TheiaIDE.app\\\" \\\"${extractedFolder}/TheiaIDE.app\\\"\"\n                                        } finally {\n                                          sh \"hdiutil detach \\\"${mountPoint}\\\"\"\n                                        }\n                                        \n                                        // Create zip with ditto for proper permissions\n                                        sh \"ditto -c -k \\\"${extractedFolder}\\\" \\\"${rezippedFile}\\\"\"\n                                        sh \"rm -f \\\"${finalZip}\\\"\"\n                                        sh \"mv \\\"${rezippedFile}\\\" \\\"${finalZip}\\\"\"\n                                        \n                                        // Cleanup\n                                        sh \"rm -rf \\\"${extractedFolder}\\\" \\\"${mountPoint}\\\"\"\n                                    }\n                                }\n                                archiveArtifacts artifacts: \"applications/electron/dist/mac-x64/**\", fingerprint: true\n                                archiveArtifacts artifacts: \"applications/electron/dist/mac-arm64/**\", fingerprint: true\n                            }\n                        }\n                    }\n                }\n                stage('Windows: Sign') {\n                    agent {\n                        kubernetes {\n                            yaml \"\"\"\napiVersion: v1\nkind: Pod\nspec:\n  podRetention: never()\n  containers:\n  - name: theia-dev\n    image: eclipsetheia/theia-blueprint:builder\n    imagePullPolicy: Always\n    command:\n    - cat\n    tty: true\n    resources:\n      limits:\n        memory: \"2000Mi\"\n        cpu: \"1000m\"\n      requests:\n        memory: \"512Mi\"\n        cpu: \"200m\"\n    volumeMounts:\n    - name: global-cache\n      mountPath: /.cache\n    - name: global-yarn\n      mountPath: /.yarn      \n    - name: global-npm\n      mountPath: /.npm      \n    - name: electron-cache\n      mountPath: /.electron-gyp\n  - name: jnlp\n    resources:\n      limits:\n        memory: \"1000Mi\"\n        cpu: \"1000m\"\n      requests:\n        memory: \"512Mi\"\n        cpu: \"250m\"\n    volumeMounts:\n    - name: volume-known-hosts\n      mountPath: /home/jenkins/.ssh\n  volumes:\n  - name: global-cache\n    emptyDir: {}\n  - name: global-yarn\n    emptyDir: {}\n  - name: global-npm\n    emptyDir: {}\n  - name: electron-cache\n    emptyDir: {}\n  - name: volume-known-hosts\n    configMap:\n      name: known-hosts\n\"\"\"\n                        }\n                    }\n                    steps {\n                        checkout scm\n                        // Clean and prepare target folder\n                        sh \"rm -rf applications/electron/dist\"\n                        sh \"mkdir -p applications/electron/dist\"\n                        \n                        echo \"Fetching artifacts from 'theia-ide-release' build #${params.BUILD_NUMBER_PARAM}\"\n                        \n                        // Retry copy artifacts with timeout to handle large files (up to 800 MiB)\n                        retry(3) {\n                            timeout(time: 20, unit: 'MINUTES') {\n                                copyArtifacts(\n                                    projectName: \"theia-ide-release\",\n                                    selector: specific(\"${params.BUILD_NUMBER_PARAM}\"),\n                                    filter: 'applications/electron/dist/windows/*',\n                                    fingerprintArtifacts: true\n                                )\n                            }\n                        }\n                        \n                        container('theia-dev') {\n                            withCredentials([string(credentialsId: \"github-bot-token\", variable: 'GITHUB_TOKEN')]) {\n                                script {\n                                    signInstaller('exe', 'windows', 'windows')\n                                }\n                            }\n                        }\n                        // Archive signed Windows artifacts\n                        archiveArtifacts artifacts: \"applications/electron/dist/windows/*\", fingerprint: true\n                    }\n                }\n            }\n        }\n    }\n    post {\n        success {\n            script {\n                build job: 'theia-ide-upload', \n                        parameters: [\n                            string(name: 'BUILD_NUMBER_PARAM', value: \"${params.BUILD_NUMBER_PARAM}\"),\n                            string(name: 'SIGN_NUMBER_PARAM', value: \"${BUILD_NUMBER}\"),\n                            booleanParam(name: 'DRY_RUN', value: params.DRY_RUN)\n                        ],\n                        wait: false\n            }\n        }\n    }\n}\n\ndef signInstaller(String ext, String os, String arch = '') {\n    // Adjust the dist folder to include architecture if supplied\n    String targetFolder = arch ? \"applications/electron/dist/${arch}\" : \"applications/electron/dist\"\n    List installers = findFiles(glob: \"${targetFolder}/*.${ext}\")\n\n    // Get the appropriate signing service URL\n    String url\n    if (os == 'mac') {\n        url = 'https://cbi.eclipse.org/macos/codesign/sign'\n    } else if (os == 'windows') {\n        url = 'https://cbi.eclipse.org/authenticode/sign'\n    } else {\n        error(\"Error during signing: unsupported OS: ${os}\")\n    }\n\n    if (installers.size() == 1) {\n        sh \"curl -o ${targetFolder}/signed-${installers[0].name} -F file=@${installers[0].path} ${url}\"\n        sh \"rm ${installers[0].path}\"\n        sh \"mv ${targetFolder}/signed-${installers[0].name} ${installers[0].path}\"\n    } else {\n        error(\"Error during signing: installer not found or multiple installers exist: ${installers.size()}\")\n    }\n}\n\ndef notarizeInstaller(String ext, String arch = '') {\n    String service = 'https://cbi.eclipse.org/macos/xcrun'\n\n    // Adjust the dist folder to include architecture if supplied\n    String targetFolder = arch ? \"applications/electron/dist/${arch}\" : \"applications/electron/dist\"\n    List installers = findFiles(glob: \"${targetFolder}/*.${ext}\")\n\n    if (installers.size() != 1) {\n        error(\"Error during notarization: installer not found or multiple installers exist: ${installers.size()}\")\n    }\n\n    // Submit for notarization\n    String response = sh(\n        script: \"curl -sS -X POST -F file=@${installers[0].path} -F 'options={\\\"primaryBundleId\\\":\\\"eclipse.theia\\\",\\\"staple\\\":true};type=application/json' ${service}/notarize\",\n        returnStdout: true\n    ).trim()\n\n    Map json = readJSON(text: response)\n    String uuid = json.uuid as String\n    String status = json.notarizationStatus?.status as String\n\n    // Poll with timeout\n    timeout(time: 20, unit: 'MINUTES') {\n        waitUntil {\n            echo \"notarization status: ${status}\"\n            if (status == 'IN_PROGRESS') {\n                sleep 60\n                response = sh(script: \"curl -sS ${service}/${uuid}/status\", returnStdout: true).trim()\n                try {\n                  def poll = readJSON(text: response)\n                  status = poll.notarizationStatus?.status as String\n                } catch (e) {\n                  error \"Status returned non-JSON:\\n${response}\"\n                }\n                return false\n            }\n            return true\n        }\n    }\n\n    if (status != 'COMPLETE') {\n        error(\"Failed to notarize ${installers[0].name}: ${response}\")\n    }\n\n    // Download notarized file\n    sh \"curl -sS -o '${targetFolder}/stapled-${installers[0].name}' ${service}/${uuid}/download\"\n    sh \"rm '${installers[0].path}'\"\n    sh \"mv '${targetFolder}/stapled-${installers[0].name}' '${installers[0].path}'\"\n}\n"
  },
  {
    "path": "releng/preview/Jenkinsfile.upload",
    "content": "/**\n * This Jenkinsfile handles updating metadata and uploading the Theia installers\n * It's designed to be run automatically after the sign job\n */\n\nimport groovy.json.JsonSlurper\n\npipeline {\n    agent none\n    options {\n        timeout(time: 5, unit: 'HOURS')\n        disableConcurrentBuilds()\n        durabilityHint('MAX_SURVIVABILITY')\n    }\n    parameters {\n        string(name: 'BUILD_NUMBER_PARAM', defaultValue: '', description: 'The build number from the upstream build job')\n        string(name: 'SIGN_NUMBER_PARAM', defaultValue: '', description: 'The build number from the upstream sign job')\n        booleanParam(name: 'DRY_RUN', defaultValue: true, description: 'If true, treat this as a dry run (no deployment of artifacts)')\n    }\n    environment {\n        THEIA_IDE_JENKINS_CI = 'true'\n        NODE_OPTIONS = '--max_old_space_size=4096'\n    }\n    stages {\n      stage('Update and Upload') {\n        parallel {\n            stage('Linux: Upload') {\n                agent any\n                steps {\n                    checkout scm\n                    echo \"Fetching Linux artifacts from build job #${params.BUILD_NUMBER_PARAM}\"\n                    \n                    // Retry copy artifacts with timeout to handle large files (up to 800 MiB)\n                    retry(3) {\n                        timeout(time: 20, unit: 'MINUTES') {\n                            copyArtifacts(\n                                projectName: 'theia-ide-release',\n                                selector: specific(\"${params.BUILD_NUMBER_PARAM}\"),\n                                filter: 'applications/electron/dist/linux/*',\n                                target: '.',\n                                fingerprintArtifacts: true\n                            )\n                        }\n                    }\n\n                    script {\n                        uploadInstaller('linux', 'linux')\n                    }\n                }\n            }\n            stage('Mac: Update Metadata and Upload') {\n                agent {\n                    kubernetes {\n                        yaml \"\"\"\napiVersion: v1\nkind: Pod\nspec:\n  podRetention: never()\n  containers:\n  - name: theia-dev\n    image: eclipsetheia/theia-blueprint:builder\n    imagePullPolicy: Always\n    command:\n    - cat\n    tty: true\n    resources:\n      limits:\n        memory: \"1700Mi\"\n        cpu: \"1000m\"\n      requests:\n        memory: \"512Mi\"\n        cpu: \"200m\"\n    volumeMounts:\n    - name: global-cache\n      mountPath: /.cache\n    - name: global-yarn\n      mountPath: /.yarn      \n    - name: global-npm\n      mountPath: /.npm      \n    - name: electron-cache\n      mountPath: /.electron-gyp\n  - name: jnlp\n    resources:\n      limits:\n        memory: \"2300Mi\"\n        cpu: \"1000m\"\n      requests:\n        memory: \"512Mi\"\n        cpu: \"250m\"\n    volumeMounts:\n    - name: volume-known-hosts\n      mountPath: /home/jenkins/.ssh\n  volumes:\n  - name: global-cache\n    emptyDir: {}\n  - name: global-yarn\n    emptyDir: {}\n  - name: global-npm\n    emptyDir: {}\n  - name: electron-cache\n    emptyDir: {}\n  - name: volume-known-hosts\n    configMap:\n      name: known-hosts\n\"\"\"\n                    }\n                }\n                steps {\n                    checkout scm\n                    echo \"Fetching Mac artifacts from sign job #${params.SIGN_NUMBER_PARAM}\"\n                    \n                    // Retry copy artifacts with timeout to handle large files (up to 800 MiB)\n                    retry(3) {\n                        timeout(time: 20, unit: 'MINUTES') {\n                            copyArtifacts(\n                                projectName: 'theia-ide-sign-notarize',\n                                selector: specific(\"${params.SIGN_NUMBER_PARAM}\"),\n                                filter: 'applications/electron/dist/mac-x64/**',\n                                target: '.',\n                                fingerprintArtifacts: true\n                            )\n                        }\n                    }\n                    \n                    container('theia-dev') {\n                        withCredentials([string(credentialsId: \"github-bot-token\", variable: 'GITHUB_TOKEN')]) {\n                            script {\n                                def packageJSON = readJSON file: \"package.json\"\n                                String version = \"${packageJSON.version}\"\n                                updateMetadata('mac-x64/TheiaIDE-' + version + '-mac.zip', 'mac-x64/latest-mac.yml', 'macos', false, '.zip', 1200)\n                                updateMetadata('mac-x64/TheiaIDE.dmg', 'mac-x64/latest-mac.yml', 'macos', false, '.dmg', 1200)\n                            }\n                        }\n                    }\n                    container('jnlp') {\n                        script {\n                            uploadInstaller('macos', 'mac-x64')\n                        }\n                    }\n                }\n            }\n            stage('Mac-Arm: Update Metadata and Upload') {\n                agent {\n                    kubernetes {\n                        yaml \"\"\"\napiVersion: v1\nkind: Pod\nspec:\n  podRetention: never()\n  containers:\n  - name: theia-dev\n    image: eclipsetheia/theia-blueprint:builder\n    imagePullPolicy: Always\n    command:\n    - cat\n    tty: true\n    resources:\n      limits:\n        memory: \"1700Mi\"\n        cpu: \"1000m\"\n      requests:\n        memory: \"512Mi\"\n        cpu: \"200m\"\n    volumeMounts:\n    - name: global-cache\n      mountPath: /.cache\n    - name: global-yarn\n      mountPath: /.yarn      \n    - name: global-npm\n      mountPath: /.npm      \n    - name: electron-cache\n      mountPath: /.electron-gyp\n  - name: jnlp\n    resources:\n      limits:\n        memory: \"2300Mi\"\n        cpu: \"1000m\"\n      requests:\n        memory: \"512Mi\"\n        cpu: \"250m\"\n    volumeMounts:\n    - name: volume-known-hosts\n      mountPath: /home/jenkins/.ssh\n  volumes:\n  - name: global-cache\n    emptyDir: {}\n  - name: global-yarn\n    emptyDir: {}\n  - name: global-npm\n    emptyDir: {}\n  - name: electron-cache\n    emptyDir: {}\n  - name: volume-known-hosts\n    configMap:\n      name: known-hosts\n\"\"\"\n                    }\n                }\n                steps {\n                    checkout scm\n                    echo \"Fetching Mac-Arm artifacts from sign job #${params.SIGN_NUMBER_PARAM}\"\n                    \n                    // Retry copy artifacts with timeout to handle large files (up to 800 MiB)\n                    retry(3) {\n                        timeout(time: 20, unit: 'MINUTES') {\n                            copyArtifacts(\n                                projectName: 'theia-ide-sign-notarize',\n                                selector: specific(\"${params.SIGN_NUMBER_PARAM}\"),\n                                filter: 'applications/electron/dist/mac-arm64/**',\n                                target: '.',\n                                fingerprintArtifacts: true\n                            )\n                        }\n                    }\n\n                    container('theia-dev') {\n                        withCredentials([string(credentialsId: \"github-bot-token\", variable: 'GITHUB_TOKEN')]) {\n                            script {\n                                def packageJSON = readJSON file: \"package.json\"\n                                String version = \"${packageJSON.version}\"\n                                updateMetadata('mac-arm64/TheiaIDE-' + version + '-arm64-mac.zip', 'mac-arm64/latest-mac.yml', 'macos-arm', false, '.zip', 1200)\n                                updateMetadata('mac-arm64/TheiaIDE.dmg', 'mac-arm64/latest-mac.yml', 'macos-arm', false, '.dmg', 1200)\n                            }\n                        }\n                    }\n                    container('jnlp') {\n                        script {\n                            uploadInstaller('macos-arm', 'mac-arm64')\n                        }\n                    }\n                }\n            }\n            stage('Windows: Update Metadata and Upload') {\n                agent {\n                    kubernetes {\n                        yaml \"\"\"\napiVersion: v1\nkind: Pod\nspec:\n  podRetention: never()\n  containers:\n  - name: theia-dev\n    image: eclipsetheia/theia-blueprint:builder\n    imagePullPolicy: Always\n    command:\n    - cat\n    tty: true\n    resources:\n      limits:\n        memory: \"1700Mi\"\n        cpu: \"1000m\"\n      requests:\n        memory: \"512Mi\"\n        cpu: \"200m\"\n    volumeMounts:\n    - name: global-cache\n      mountPath: /.cache\n    - name: global-yarn\n      mountPath: /.yarn      \n    - name: global-npm\n      mountPath: /.npm      \n    - name: electron-cache\n      mountPath: /.electron-gyp\n  - name: jnlp\n    resources:\n      limits:\n        memory: \"1000Mi\"\n        cpu: \"1000m\"\n      requests:\n        memory: \"512Mi\"\n        cpu: \"250m\"\n    volumeMounts:\n    - name: volume-known-hosts\n      mountPath: /home/jenkins/.ssh\n  volumes:\n  - name: global-cache\n    emptyDir: {}\n  - name: global-yarn\n    emptyDir: {}\n  - name: global-npm\n    emptyDir: {}\n  - name: electron-cache\n    emptyDir: {}\n  - name: volume-known-hosts\n    configMap:\n      name: known-hosts\n\"\"\"\n                    }\n                }\n                steps {\n                    checkout scm\n                    echo \"Fetching Windows artifacts from sign job #${params.SIGN_NUMBER_PARAM}\"\n                    \n                    // Retry copy artifacts with timeout to handle large files (up to 800 MiB)\n                    retry(3) {\n                        timeout(time: 20, unit: 'MINUTES') {\n                            copyArtifacts(\n                                projectName: 'theia-ide-sign-notarize',\n                                selector: specific(\"${params.SIGN_NUMBER_PARAM}\"),\n                                filter: 'applications/electron/dist/windows/*',\n                                target: '.',\n                                fingerprintArtifacts: true\n                            )\n                        }\n                    }\n\n                    container('theia-dev') {\n                        withCredentials([string(credentialsId: \"github-bot-token\", variable: 'GITHUB_TOKEN')]) {\n                            script {\n                                updateMetadata('windows/TheiaIDESetup.exe', 'windows/latest.yml', 'windows', true, '.exe', 1200)\n                            }\n                        }\n                    }\n                    container('jnlp') {\n                        script {\n                            echo 'Computing updatable versions before uploading new installer'\n                            def updatableVersions = getUpdatableVersions()\n                            echo 'updatableVersions: ' + updatableVersions\n                            uploadInstaller('windows', 'windows')\n                            copyInstallerAndUpdateLatestYml('windows', 'TheiaIDESetup', 'exe', 'latest.yml', updatableVersions)\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\ndef updateMetadata(String executable, String yaml, String platform, Boolean updatePaths, String fileExtension, int sleepBetweenRetries) {\n    int maxRetry = 4\n    try {\n        // Install dependencies and update metadata\n        sh \"yarn install --network-timeout 100000 --force\"\n        sh \"yarn electron update:blockmap -e ${executable}\"\n        sh \"yarn electron update:checksum -e ${executable} -y ${yaml} -p ${platform} -u ${updatePaths} -f ${fileExtension}\"\n    } catch (error) {\n        retry(maxRetry) {\n            sleep(sleepBetweenRetries)\n            echo \"yarn failed - Retrying\"\n            sh \"yarn install --network-timeout 100000 --force\"\n            sh \"yarn electron update:blockmap -e ${executable}\"\n            sh \"yarn electron update:checksum -e ${executable} -y ${yaml} -p ${platform} -u ${updatePaths} -f ${fileExtension}\"\n        }\n    }\n}\n\ndef uploadInstaller(String platform, String folder = '') {\n    if (!isDryRun()) {\n        String targetFolder = folder ? \"applications/electron/dist/${folder}\" : \"applications/electron/dist\"\n        def packageJSON = readJSON file: \"package.json\"\n        String version = \"${packageJSON.version}\"\n        \n        sshagent(['projects-storage.eclipse.org-bot-ssh']) {\n            // Remove and recreate version-specific folder\n            sh \"ssh genie.theia@projects-storage.eclipse.org rm -rf /home/data/httpd/download.eclipse.org/theia/ide-preview/${version}/${platform}\"\n            sh \"ssh genie.theia@projects-storage.eclipse.org mkdir -p /home/data/httpd/download.eclipse.org/theia/ide-preview/${version}/${platform}\"\n            sh \"scp ${targetFolder}/*.* genie.theia@projects-storage.eclipse.org:/home/data/httpd/download.eclipse.org/theia/ide-preview/${version}/${platform}\"\n            \n            // Remove and recreate latest folder\n            sh \"ssh genie.theia@projects-storage.eclipse.org rm -rf /home/data/httpd/download.eclipse.org/theia/ide-preview/latest/${platform}\"\n            sh \"ssh genie.theia@projects-storage.eclipse.org mkdir -p /home/data/httpd/download.eclipse.org/theia/ide-preview/latest/${platform}\"\n            sh \"scp ${targetFolder}/*.* genie.theia@projects-storage.eclipse.org:/home/data/httpd/download.eclipse.org/theia/ide-preview/latest/${platform}\"\n        }\n    } else {\n        echo \"Skipped upload for dry run\"\n        echo \"Would have uploaded the following artifacts\"\n        sh \"ls -l applications/electron/dist/${folder}\"\n    }\n}\n\n/**\n * List all directories in the ide-preview directory that represent versions.\n * Only include version numbers lower than the current version.\n */\ndef getUpdatableVersions() {\n    def packageJSON = readJSON file: \"package.json\"\n    String currentVersion = \"${packageJSON.version}\"\n    \n    def versions = ''\n    \n    sshagent(['projects-storage.eclipse.org-bot-ssh']) {\n        versions = sh(\n            script: \"\"\"\n            ssh genie.theia@projects-storage.eclipse.org \"cd /home/data/httpd/download.eclipse.org/theia/ide-preview/ && \\\n            find . -maxdepth 1 -type d -regex '.*/[0-9]+\\\\.[0-9]+\\\\.[0-9]+' -exec basename {} \\\\; | sort -V | awk -v curVer='${currentVersion}' '{\n                if (\\\\\\$1 != curVer && \\\\\\$1 < curVer) print \\\\\\$1\n            }' | paste -sd ','\"\n            \"\"\",\n            returnStdout: true\n        ).trim()\n    }\n    \n    return versions\n}\n\n/**\n * For Windows, create a versioned copy of the installer for updates\n * and update the latest.yml file for older versions.\n */\ndef copyInstallerAndUpdateLatestYml(String platform, String installer, String extension, String yaml, String updatableVersions) {\n    if (!isDryRun()) {\n        def packageJSON = readJSON file: \"package.json\"\n        String version = \"${packageJSON.version}\"\n        \n        sshagent(['projects-storage.eclipse.org-bot-ssh']) {\n            // Create versioned copies of installer and blockmap files\n            sh \"ssh genie.theia@projects-storage.eclipse.org cp /home/data/httpd/download.eclipse.org/theia/ide-preview/latest/${platform}/${installer}.${extension} /home/data/httpd/download.eclipse.org/theia/ide-preview/latest/${platform}/${installer}-${version}.${extension}\"\n            sh \"ssh genie.theia@projects-storage.eclipse.org cp /home/data/httpd/download.eclipse.org/theia/ide-preview/${version}/${platform}/${installer}.${extension} /home/data/httpd/download.eclipse.org/theia/ide-preview/${version}/${platform}/${installer}-${version}.${extension}\"\n            sh \"ssh genie.theia@projects-storage.eclipse.org cp /home/data/httpd/download.eclipse.org/theia/ide-preview/latest/${platform}/${installer}.${extension}.blockmap /home/data/httpd/download.eclipse.org/theia/ide-preview/latest/${platform}/${installer}-${version}.${extension}.blockmap\"\n            sh \"ssh genie.theia@projects-storage.eclipse.org cp /home/data/httpd/download.eclipse.org/theia/ide-preview/${version}/${platform}/${installer}.${extension}.blockmap /home/data/httpd/download.eclipse.org/theia/ide-preview/${version}/${platform}/${installer}-${version}.${extension}.blockmap\"\n        }\n        \n        // Update latest.yml for older versions to enable auto-updates\n        if (updatableVersions.length() > 0) {\n            for (oldVersion in updatableVersions.split(\",\")) {\n                sshagent(['projects-storage.eclipse.org-bot-ssh']) {\n                    sh \"ssh genie.theia@projects-storage.eclipse.org rm -f /home/data/httpd/download.eclipse.org/theia/ide-preview/${oldVersion}/${platform}/${yaml}\"\n                    sh \"ssh genie.theia@projects-storage.eclipse.org cp /home/data/httpd/download.eclipse.org/theia/ide-preview/${version}/${platform}/${yaml} /home/data/httpd/download.eclipse.org/theia/ide-preview/${oldVersion}/${platform}/${yaml}\"\n                }\n            }\n        } else {\n            echo \"No updateable versions found\"\n        }\n    } else {\n        echo \"Skipped copying installer for dry run\"\n    }\n}\n\n// Helper function for dry run status\ndef isDryRun() {\n    return params.DRY_RUN\n}"
  },
  {
    "path": "releng/promote/Jenkinsfile",
    "content": "/**\n * This Jenkinsfile promotes a given version of the Theia IDE from /theia/ide-preview to /theia/ide\n */\n\n/* groovylint-disable NestedBlockDepth */\nimport groovy.json.JsonSlurper\n\npipeline {\n    agent none\n    options {\n        timeout(time: 3, unit: 'HOURS')\n        disableConcurrentBuilds()\n    }\n    stages {\n\n       stage('Setup parameters') {\n            steps {\n                script { \n                    properties([\n                        parameters([\n                            string(\n                                defaultValue: 'latest', \n                                name: 'VERSION', \n                                trim: true\n                            )\n                        ])\n                    ])\n                }\n            }\n       }\n\n        stage('Promote') {\n            agent any\n            steps {\n                script {\n                    promote('linux', params.VERSION)\n                    promote('macos', params.VERSION)\n                    promote('macos-arm', params.VERSION)\n                    promote('windows', params.VERSION)\n\n                    // update latest.yaml on windows for differential updater\n                    def updatableVersions = getUpdatableVersions(params.VERSION)\n                    echo 'updatableVersions: ' + updatableVersions\n                    updateLatestYaml('windows', params.VERSION, 'TheiaIDESetup', 'exe', 'latest.yml', updatableVersions)\n                }\n            }\n        }\n    }\n}\n\ndef promote(String platform, String version) {\n    sshagent(['projects-storage.eclipse.org-bot-ssh']) {\n        sh \"ssh genie.theia@projects-storage.eclipse.org rm -rf /home/data/httpd/download.eclipse.org/theia/ide/${version}/${platform}\"\n        sh \"ssh genie.theia@projects-storage.eclipse.org mkdir -p /home/data/httpd/download.eclipse.org/theia/ide/${version}/${platform}\"\n        sh \"ssh genie.theia@projects-storage.eclipse.org cp -a /home/data/httpd/download.eclipse.org/theia/ide-preview/${version}/${platform}/. /home/data/httpd/download.eclipse.org/theia/ide/${version}/${platform}/\"\n\n        sh \"ssh genie.theia@projects-storage.eclipse.org rm -rf /home/data/httpd/download.eclipse.org/theia/ide/latest/${platform}\"\n        sh \"ssh genie.theia@projects-storage.eclipse.org mkdir -p /home/data/httpd/download.eclipse.org/theia/ide/latest/${platform}\"\n        sh \"ssh genie.theia@projects-storage.eclipse.org cp -a /home/data/httpd/download.eclipse.org/theia/ide-preview/${version}/${platform}/. /home/data/httpd/download.eclipse.org/theia/ide/latest/${platform}/\"\n    }\n}\n\n// copies updated (checksum, link to latest version) metadata yaml to older versions\ndef updateLatestYaml(String platform, String version, String installer, String extension, String yaml, String UPDATABLE_VERSIONS) {\n    if (UPDATABLE_VERSIONS.length() != 0) {\n        for (oldVersion in UPDATABLE_VERSIONS.split(\",\")) {\n            sshagent(['projects-storage.eclipse.org-bot-ssh']) {\n                sh \"ssh genie.theia@projects-storage.eclipse.org rm -f /home/data/httpd/download.eclipse.org/theia/ide/${oldVersion}/${platform}/${yaml}\"\n                sh \"ssh genie.theia@projects-storage.eclipse.org cp /home/data/httpd/download.eclipse.org/theia/ide/${version}/${platform}/${yaml} /home/data/httpd/download.eclipse.org/theia/ide/${oldVersion}/${platform}/${yaml}\"\n            }\n        }\n    } else {\n        echo \"No updateable versions\"\n    }\n}\n\n/**\n * List all directories in the ide directory. \n * Only takes the ones with a version identifier name. \n * Only take version numbers lower than the current version. \n */\ndef getUpdatableVersions(String currentVersion) {\n    def versions = ''\n    \n    sshagent(['projects-storage.eclipse.org-bot-ssh']) {\n        versions = sh(\n            script: \"\"\"\n            ssh genie.theia@projects-storage.eclipse.org \"cd /home/data/httpd/download.eclipse.org/theia/ide/ && \\\n            find . -maxdepth 1 -type d -regex '.*/[0-9]+\\\\.[0-9]+\\\\.[0-9]+' -exec basename {} \\\\; | sort -V | awk -v curVer='${currentVersion}' '{\n                if (\\\\\\$1 != curVer && \\\\\\$1 < curVer) print \\\\\\$1\n            }' | paste -sd ','\"\n            \"\"\",\n            returnStdout: true\n        ).trim()\n    }\n    \n    return versions\n}\n"
  },
  {
    "path": "scripts/build-with-local-theia.js",
    "content": "#!/usr/bin/env node\n\n/**\n * Script to build Theia IDE with a local Theia framework development version.\n *\n * This allows testing Theia IDE changes against local Theia framework modifications\n * without publishing to npm first. It expects a locally cloned version of the\n * Theia repository (https://github.com/eclipse-theia/theia) that will be linked\n * into the Theia IDE build.\n *\n * Usage:\n *   node scripts/build-with-local-theia.js [options]\n *\n * Options:\n *   --theia-path <path>   Path to local Theia repository [default: ../theia]\n *   --skip-theia-build    Skip building Theia packages (use if already built) [default: false]\n *   --skip-ide-build      Skip building Theia IDE (use for linking only) [default: false]\n *   --skip-plugins        Skip downloading plugins [default: false]\n *   --package             Package the electron-next application after building [default: false]\n *   --unlink              Remove links and restore npm dependencies [default: false]\n *   --dry-run             Print commands without executing them [default: false]\n *   --help                Show this help message\n *\n * See docs/developing-with-local-theia.md for detailed documentation.\n */\n\nconst { execSync } = require('child_process');\nconst fs = require('fs');\nconst path = require('path');\n\nconst ROOT_DIR = path.resolve(__dirname, '..');\nconst DEFAULT_THEIA_PATH = path.resolve(ROOT_DIR, '..', 'theia');\n\n// Parse command line arguments\nconst args = process.argv.slice(2);\n\nfunction getArgValue(flag, defaultValue) {\n    const index = args.indexOf(flag);\n    if (index !== -1 && args[index + 1]) {\n        return args[index + 1];\n    }\n    return defaultValue;\n}\n\nconst options = {\n    theiaPath: path.resolve(getArgValue('--theia-path', DEFAULT_THEIA_PATH)),\n    skipTheiaBuild: args.includes('--skip-theia-build'),\n    skipIdeBuild: args.includes('--skip-ide-build'),\n    skipPlugins: args.includes('--skip-plugins'),\n    package: args.includes('--package'),\n    unlink: args.includes('--unlink'),\n    dryRun: args.includes('--dry-run'),\n    help: args.includes('--help')\n};\n\nif (options.help) {\n    console.log(`\nUsage: node scripts/build-with-local-theia.js [options]\n\nThis script builds the Theia IDE against a local Theia framework checkout,\nallowing you to test framework changes without publishing to npm first.\n\nThe script uses yarn link to connect the local Theia packages to the IDE build.\n\nNote: This script does not update the IDE version or Theia package versions.\nIt uses the current state of both repositories. If needed, you can run\nversioning commands (e.g., yarn update:theia) separately before building.\n\nPrerequisites:\n  A local clone of the Theia repository (https://github.com/eclipse-theia/theia).\n  By default, the script expects it at ../theia (next to the theia-ide directory).\n\nOptions:\n  --theia-path <path>   Path to local Theia repository [default: ../theia]\n  --skip-theia-build    Skip building Theia packages (use if already built) [default: false]\n  --skip-ide-build      Skip building Theia IDE (use for linking only) [default: false]\n  --skip-plugins        Skip downloading plugins [default: false]\n  --package             Package the electron-next application after building [default: false]\n  --unlink              Remove links and restore npm dependencies [default: false]\n  --dry-run             Print commands without executing them [default: false]\n  --help                Show this help message\n\nSee docs/developing-with-local-theia.md for detailed documentation and examples.\n`);\n    process.exit(0);\n}\n\n/**\n * Execute a shell command\n */\nfunction run(cmd, cwd = ROOT_DIR, description = '') {\n    if (description) {\n        console.log(`\\n${'='.repeat(60)}`);\n        console.log(`> ${description}`);\n        console.log(`${'='.repeat(60)}`);\n    }\n    console.log(`$ ${cmd}`);\n    console.log(`  (in ${cwd})\\n`);\n\n    if (options.dryRun) {\n        console.log('[DRY RUN] Command not executed');\n        return '';\n    }\n\n    try {\n        execSync(cmd, {\n            cwd,\n            stdio: 'inherit',\n            env: {\n                ...process.env,\n                NODE_OPTIONS: '--max_old_space_size=4096'\n            }\n        });\n    } catch (error) {\n        console.error(`\\nCommand failed: ${cmd}`);\n        throw error;\n    }\n}\n\n/**\n * Read JSON file\n */\nfunction readJson(filePath) {\n    return JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n}\n\n/**\n * Get all @theia/* packages from a package.json\n */\nfunction getTheiaDependencies(packageJsonPath) {\n    const pkg = readJson(packageJsonPath);\n    const theiaDeps = new Set();\n\n    for (const deps of [pkg.dependencies, pkg.devDependencies]) {\n        if (deps) {\n            for (const dep of Object.keys(deps)) {\n                if (dep.startsWith('@theia/')) {\n                    theiaDeps.add(dep);\n                }\n            }\n        }\n    }\n\n    return theiaDeps;\n}\n\n/**\n * Find all Theia packages in the local Theia repository\n */\nfunction findTheiaPackages(theiaPath) {\n    const packagesDir = path.join(theiaPath, 'packages');\n    const devPackagesDir = path.join(theiaPath, 'dev-packages');\n    const packages = new Map();\n\n    for (const dir of [packagesDir, devPackagesDir]) {\n        if (!fs.existsSync(dir)) {\n            continue;\n        }\n\n        for (const entry of fs.readdirSync(dir)) {\n            const pkgJsonPath = path.join(dir, entry, 'package.json');\n            if (fs.existsSync(pkgJsonPath)) {\n                const pkg = readJson(pkgJsonPath);\n                if (pkg.name && pkg.name.startsWith('@theia/')) {\n                    packages.set(pkg.name, path.join(dir, entry));\n                }\n            }\n        }\n    }\n\n    return packages;\n}\n\n/**\n * Collect all @theia/* dependencies from IDE packages\n */\nfunction collectAllTheiaDependencies() {\n    const allDeps = new Set();\n\n    // Root package.json\n    const rootDeps = getTheiaDependencies(path.join(ROOT_DIR, 'package.json'));\n    rootDeps.forEach(d => allDeps.add(d));\n\n    // Applications\n    for (const app of ['electron', 'browser', 'electron-next']) {\n        const appPkgPath = path.join(ROOT_DIR, 'applications', app, 'package.json');\n        if (fs.existsSync(appPkgPath)) {\n            getTheiaDependencies(appPkgPath).forEach(d => allDeps.add(d));\n        }\n    }\n\n    // Extensions\n    for (const ext of ['launcher', 'product', 'updater']) {\n        const extPkgPath = path.join(ROOT_DIR, 'theia-extensions', ext, 'package.json');\n        if (fs.existsSync(extPkgPath)) {\n            getTheiaDependencies(extPkgPath).forEach(d => allDeps.add(d));\n        }\n    }\n\n    return allDeps;\n}\n\n/**\n * Build Theia\n */\nfunction buildTheia() {\n    run('npm ci', options.theiaPath, 'Install Theia dependencies');\n    // Compile all Theia packages, no need to build the applications in this case\n    run('npm run compile', options.theiaPath, 'Compile Theia packages');\n}\n\n/**\n * Create yarn link for Theia packages\n */\nfunction linkTheiaPackages() {\n    console.log(`\\n${'='.repeat(60)}`);\n    console.log('> Creating yarn links for Theia packages');\n    console.log(`${'='.repeat(60)}\\n`);\n\n    const theiaPackages = findTheiaPackages(options.theiaPath);\n    const requiredDeps = collectAllTheiaDependencies();\n\n    console.log(`Found ${theiaPackages.size} Theia packages`);\n    console.log(`Theia IDE requires ${requiredDeps.size} @theia/* packages\\n`);\n\n    const linked = [];\n    const missing = [];\n\n    // Create links in Theia packages\n    for (const dep of requiredDeps) {\n        const pkgPath = theiaPackages.get(dep);\n        if (pkgPath) {\n            if (!options.dryRun) {\n                console.log(`Linking ${dep}...`);\n                try {\n                    execSync('yarn link', { cwd: pkgPath, stdio: 'pipe' });\n                } catch (e) {\n                    // Link might already exist, that's fine\n                }\n            }\n            linked.push(dep);\n        } else {\n            missing.push(dep);\n        }\n    }\n\n    if (options.dryRun) {\n        console.log(`Would create yarn links for ${linked.length} packages`);\n    }\n\n    if (missing.length > 0) {\n        console.log('\\nWarning: The following packages were not found in local Theia:');\n        missing.forEach(m => console.log(`  - ${m}`));\n    }\n\n    // Link packages in IDE\n    console.log(`\\nLinking ${linked.length} packages in IDE...`);\n    if (linked.length > 0) {\n        const linkCmd = `yarn link ${linked.join(' ')}`;\n        console.log(`$ ${linkCmd}`);\n        if (!options.dryRun) {\n            execSync(linkCmd, { cwd: ROOT_DIR, stdio: 'inherit' });\n        } else {\n            console.log('[DRY RUN] Command not executed');\n        }\n    }\n\n    console.log(`\\n${options.dryRun ? 'Would link' : 'Linked'} ${linked.length} packages`);\n    return linked;\n}\n\n/**\n * Unlink Theia packages and restore npm dependencies\n */\nfunction unlinkTheiaPackages() {\n    console.log(`\\n${'='.repeat(60)}`);\n    console.log('> Removing yarn links and restoring npm dependencies');\n    console.log(`${'='.repeat(60)}\\n`);\n\n    const requiredDeps = collectAllTheiaDependencies();\n\n    // Unlink packages\n    if (options.dryRun) {\n        console.log(`Would unlink ${requiredDeps.size} packages`);\n    } else {\n        for (const dep of requiredDeps) {\n            console.log(`Unlinking ${dep}...`);\n            try {\n                execSync(`yarn unlink ${dep}`, { cwd: ROOT_DIR, stdio: 'pipe' });\n            } catch (e) {\n                // Ignore errors if not linked\n            }\n        }\n    }\n\n    // Reinstall to restore npm versions\n    run('yarn --force', ROOT_DIR, 'Reinstall npm dependencies');\n\n    console.log('\\nLinks removed and npm dependencies restored');\n}\n\n/**\n * Build IDE\n */\nfunction buildIde() {\n    run('yarn', ROOT_DIR, 'Install IDE dependencies');\n    run('yarn build:extensions', ROOT_DIR, 'Build IDE extensions');\n    run('yarn build:applications:next:dev', ROOT_DIR, 'Build electron-next application');\n    if (!options.skipPlugins) {\n        run('yarn download:plugins', ROOT_DIR, 'Download plugins');\n    } else {\n        console.log('\\nSkipping plugin download (--skip-plugins)');\n    }\n}\n\n/**\n * Package the electron-next application\n */\nfunction packageApp() {\n    run('yarn package:applications:next', ROOT_DIR, 'Package electron-next application');\n}\n\n/**\n * Main function\n */\nasync function main() {\n    console.log('\\nBuild Theia IDE with local Theia framework\\n');\n    console.log(`Theia path: ${options.theiaPath}`);\n\n    const startTime = Date.now();\n\n    if (options.unlink) {\n        unlinkTheiaPackages();\n    } else {\n        // Verify Theia exists\n        if (!fs.existsSync(options.theiaPath)) {\n            console.error(`\\nError: Theia directory not found at ${options.theiaPath}`);\n            console.error('\\nPlease clone the Theia repository first:');\n            console.error('  git clone https://github.com/eclipse-theia/theia.git ../theia');\n            console.error('\\nOr specify a different location with --theia-path');\n            process.exit(1);\n        }\n\n        // Build Theia\n        if (!options.skipTheiaBuild) {\n            buildTheia();\n        } else {\n            console.log('\\nSkipping Theia build (--skip-theia-build)');\n        }\n\n        // Link packages\n        linkTheiaPackages();\n\n        // Build IDE\n        if (!options.skipIdeBuild) {\n            buildIde();\n        } else {\n            console.log('\\nSkipping IDE build (--skip-ide-build)');\n        }\n\n        // Package if requested\n        if (options.package) {\n            packageApp();\n        }\n    }\n\n    const elapsed = ((Date.now() - startTime) / 1000 / 60).toFixed(2);\n\n    console.log(`\\n${'='.repeat(60)}`);\n    console.log('Done!');\n    console.log(`${'='.repeat(60)}`);\n    console.log(`Total time: ${elapsed} minutes`);\n\n    if (!options.unlink && !options.package) {\n        console.log(`\nNext steps:\n  - Run the IDE: yarn --cwd applications/electron-next start\n  - Make changes in Theia, rebuild: (cd ${options.theiaPath} && npm run compile)\n  - Rebuild IDE: yarn build:applications:next:dev\n  - Package the app: node scripts/build-with-local-theia.js --skip-theia-build --skip-ide-build --package\n  - When done, restore npm deps: node scripts/build-with-local-theia.js --unlink\n`);\n    }\n\n    if (options.package) {\n        console.log('\\nPackaged application is in: applications/electron-next/dist/');\n    }\n\n    if (options.dryRun) {\n        console.log('\\nThis was a dry run. No commands were actually executed.');\n    }\n}\n\nmain().catch(error => {\n    console.error('\\nBuild failed:', error.message);\n    process.exit(1);\n});\n"
  },
  {
    "path": "scripts/generate-next-icons.js",
    "content": "#!/usr/bin/env node\n// @ts-check\n\n/**\n * Generates next icons by recoloring the Theia IDE blue (#00ADEE) to\n * next purple (#8B5CF6), matching the next splash screen color scheme.\n * White text and transparency are preserved.\n *\n * Source: applications/electron/resources/icons/\n * Output: applications/electron-next/resources/icons/\n *\n * Requires ImageMagick (`convert`). Falls back to `sharp` if ImageMagick is not available.\n *\n * Usage: node scripts/generate-next-icons.js\n */\n\nconst path = require('path');\nconst fs = require('fs');\nconst child_process = require('child_process');\n\nconst SOURCE_DIR = path.resolve(__dirname, '../applications/electron/resources/icons');\nconst TARGET_DIR = path.resolve(__dirname, '../applications/electron-next/resources/icons');\n\nconst ICON_MAPPINGS = [\n    { src: 'LinuxLauncherIcons/512x512.png', dest: 'LinuxLauncherIcons/512x512.png' },\n    { src: 'WindowIcon/512-512.png', dest: 'WindowIcon/512-512.png' },\n    { src: '512x512.png', dest: '512x512.png' },\n];\n\n// Theia blue → next purple (same as splash screen logo)\nconst THEIA_BLUE = '#00ADEE';\nconst NEXT_PURPLE = '#8B5CF6';\n\nfunction hasImageMagick() {\n    try {\n        child_process.execSync('convert --version', { stdio: 'pipe' });\n        return true;\n    } catch {\n        return false;\n    }\n}\n\nfunction generateWithImageMagick(srcPath, destPath) {\n    const destDir = path.dirname(destPath);\n    fs.mkdirSync(destDir, { recursive: true });\n\n    // Replace blue with purple on the RGB data, then re-apply original alpha.\n    // The -fuzz 25% catches anti-aliased edge pixels near the blue color.\n    const cmd = [\n        'convert',\n        `\\\\( \"${srcPath}\" -alpha off -fuzz 25% -fill \"${NEXT_PURPLE}\" -opaque \"${THEIA_BLUE}\" \\\\)`,\n        `\\\\( \"${srcPath}\" -alpha extract \\\\)`,\n        '-compose CopyOpacity -composite',\n        `\"${destPath}\"`\n    ].join(' ');\n\n    child_process.execSync(cmd, { stdio: 'pipe' });\n    console.log(`Generated: ${destPath}`);\n}\n\nasync function generateWithSharp(srcPath, destPath) {\n    const sharp = require('sharp');\n    const destDir = path.dirname(destPath);\n    fs.mkdirSync(destDir, { recursive: true });\n\n    const { data, info } = await sharp(srcPath)\n        .ensureAlpha()\n        .raw()\n        .toBuffer({ resolveWithObject: true });\n\n    const { width, height, channels } = info;\n    const output = Buffer.from(data);\n\n    for (let y = 0; y < height; y++) {\n        for (let x = 0; x < width; x++) {\n            const i = (y * width + x) * channels;\n            const r = data[i];\n            const g = data[i + 1];\n            const b = data[i + 2];\n\n            // Compute saturation (HSV) to distinguish colored vs white pixels\n            const max = Math.max(r, g, b);\n            const min = Math.min(r, g, b);\n            const sat = max === 0 ? 0 : (max - min) / max;\n\n            if (sat > 0.10 && data[i + 3] > 0) {\n                // Colored (blue) pixel: replace with next purple\n                output[i] = 0x8B;\n                output[i + 1] = 0x5C;\n                output[i + 2] = 0xF6;\n            }\n        }\n    }\n\n    await sharp(output, { raw: { width, height, channels } })\n        .png()\n        .toFile(destPath);\n\n    console.log(`Generated: ${destPath}`);\n}\n\nasync function main() {\n    const useImageMagick = hasImageMagick();\n\n    if (!useImageMagick) {\n        try {\n            require('sharp');\n        } catch {\n            console.error('Error: Neither ImageMagick nor sharp is available.');\n            console.error('Install ImageMagick: sudo apt-get install imagemagick');\n            console.error('Or install sharp: npm install sharp');\n            process.exit(1);\n        }\n    }\n\n    console.log(`Using ${useImageMagick ? 'ImageMagick' : 'sharp'} for icon generation`);\n\n    for (const mapping of ICON_MAPPINGS) {\n        const srcPath = path.join(SOURCE_DIR, mapping.src);\n        const destPath = path.join(TARGET_DIR, mapping.dest);\n\n        if (!fs.existsSync(srcPath)) {\n            console.warn(`Warning: Source icon not found: ${srcPath}`);\n            continue;\n        }\n\n        if (useImageMagick) {\n            generateWithImageMagick(srcPath, destPath);\n        } else {\n            await generateWithSharp(srcPath, destPath);\n        }\n    }\n\n    console.log('Next icon generation complete!');\n}\n\nmain().catch(err => {\n    console.error('Icon generation failed:', err);\n    process.exit(1);\n});\n"
  },
  {
    "path": "scripts/make-files-writeable.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2025 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nconst argv = yargs(hideBin(process.argv))\n    .option('directory', {\n        alias: 'e',\n        type: 'string',\n        default: 'plugins',\n        description: 'The parent directory which contains the files we need to make writable',\n    })\n    .version(false)\n    .wrap(120)\n    .parseSync();\n\nexecute();\n\nasync function execute(): Promise<void> {\n    const directory = argv.directory;\n    console.log(`Input directory: ${directory}`);\n\n    try {\n        makeWritable(directory);\n    } catch (error) {\n        console.error(`Failed to make files writable: ${error.message}`);\n        process.exit(1);\n    }\n}\n\nfunction makeWritable(dir: string): void {\n    if (!fs.existsSync(dir)) {\n        throw new Error(`Directory '${dir}' does not exist.`);\n    }\n\n    const entries = fs.readdirSync(dir, { withFileTypes: true });\n\n    for (const entry of entries) {\n        const fullPath = path.join(dir, entry.name);\n        if (entry.isDirectory()) {\n            makeWritable(fullPath);\n        } else if (entry.isFile()) {\n            const stats = fs.statSync(fullPath);\n            const isWritable = (stats.mode & 0o200) !== 0;\n            if (!isWritable) {\n                const isExecutable = (stats.mode & 0o111) !== 0;\n                const newMode = isExecutable ? 0o755 : 0o644;\n                console.log(`Making '${fullPath}' writable with mode '${isExecutable ? '0o755' : '0o644'}'`);\n                fs.chmodSync(fullPath, newMode);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "scripts/update-theia-version.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2021 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { PackageJson } from 'type-fest';\n\nexecute();\n\nasync function execute(): Promise<void> {\n    const theiaVersion = process.argv[2];\n    const packageJsonPath = path.resolve(\n        './',\n        'package.json'\n    );\n\n    console.log(`Updating ${packageJsonPath}...`);\n\n    const packageJsonContents: string = fs.readFileSync(packageJsonPath, { encoding: 'utf8' });\n    const packageJson: PackageJson = JSON.parse(packageJsonContents);\n\n    console.log('...dependencies...');\n    if (packageJson.dependencies) {\n        updateTheiaVersions(packageJson.dependencies, theiaVersion);\n    }\n    console.log('...done...');\n\n    console.log('...devDependencies...');\n    if (packageJson.devDependencies) {\n        updateTheiaVersions(packageJson.devDependencies, theiaVersion);\n    }\n    console.log('...done.');\n\n    // note: \"null\" is valid as per `stringify()` signature\n    // eslint-disable-next-line no-null/no-null\n    fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));\n}\n\nfunction updateTheiaVersions(dependencies: PackageJson.Dependency, theiaVersion: string): void {\n    for (const dependency in dependencies) {\n        if (dependency.startsWith('@theia/')) {\n            console.log(`...setting ${dependency} from ${dependencies[dependency]} to next...`);\n            dependencies[dependency] = theiaVersion;\n        }\n    }\n}\n"
  },
  {
    "path": "theia-extensions/launcher/.eslintrc.js",
    "content": "/** @type {import('eslint').Linter.Config} */\nmodule.exports = {\n    extends: [\n        '../../configs/build.eslintrc.json'\n    ],\n    parserOptions: {\n        tsconfigRootDir: __dirname,\n        project: 'tsconfig.json'\n    }\n};\n"
  },
  {
    "path": "theia-extensions/launcher/package.json",
    "content": "{\n  \"name\": \"theia-ide-launcher-ext\",\n  \"version\": \"1.71.100\",\n  \"keywords\": [\n    \"theia-extension\"\n  ],\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/eclipse-theia/theia-ide.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/eclipse-theia/theia-ide/issues\"\n  },\n  \"homepage\": \"https://github.com/eclipse-theia/theia-ide\",\n  \"files\": [\n    \"lib\",\n    \"src\"\n  ],\n  \"dependencies\": {\n    \"@theia/core\": \"1.72.0-next.20\",\n    \"@vscode/sudo-prompt\": \"9.3.1\",\n    \"body-parser\": \"^1.20.5\",\n    \"fs-extra\": \"^4.0.3\"\n  },\n  \"devDependencies\": {\n    \"rimraf\": \"^2.7.1\",\n    \"typescript\": \"^4.9.5\"\n  },\n  \"scripts\": {\n    \"clean\": \"rimraf lib *.tsbuildinfo\",\n    \"build\": \"tsc -b\",\n    \"lint\": \"eslint --ext js,jsx,ts,tsx src\",\n    \"lint:fix\": \"eslint --ext js,jsx,ts,tsx src --fix\",\n    \"watch\": \"tsc -w\",\n    \"update:theia\": \"ts-node ../../scripts/update-theia-version.ts\",\n    \"update:next\": \"ts-node ../../scripts/update-theia-version.ts next\"\n  },\n  \"theiaExtensions\": [\n    {\n      \"frontendElectron\": \"lib/browser/create-launcher-frontend-module\",\n      \"backend\": \"lib/node/launcher-backend-module\"\n    }\n  ]\n}"
  },
  {
    "path": "theia-extensions/launcher/src/browser/create-launcher-contribution.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2022-2024 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { ConfirmDialog, Dialog, FrontendApplication, FrontendApplicationContribution, StorageService } from '@theia/core/lib/browser';\nimport { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';\nimport { ILogger, MaybePromise } from '@theia/core/lib/common';\nimport { nls } from '@theia/core/lib/common/nls';\nimport { inject, injectable } from '@theia/core/shared/inversify';\nimport { LauncherService } from './launcher-service';\nimport { DesktopFileService } from './desktopfile-service';\n\n@injectable()\nexport class CreateLauncherCommandContribution implements FrontendApplicationContribution {\n\n    @inject(StorageService)\n    protected readonly storageService: StorageService;\n\n    @inject(ILogger)\n    protected readonly logger: ILogger;\n\n    @inject(LauncherService) private readonly launcherService: LauncherService;\n\n    @inject(DesktopFileService) private readonly desktopFileService: DesktopFileService;\n\n    onStart(_app: FrontendApplication): MaybePromise<void> {\n        const appConfig = FrontendApplicationConfigProvider.get();\n        const applicationName = appConfig.applicationName;\n        const uriScheme = appConfig.electron.uriScheme;\n\n        this.launcherService.isInitialized(uriScheme).then(async initialized => {\n            if (!initialized) {\n                const messageContainer = document.createElement('div');\n                // eslint-disable-next-line max-len\n                messageContainer.textContent = nls.localizeByDefault(`Would you like to install a shell command that launches the application?\\nYou will be able to run ${applicationName} from the command line by typing '${uriScheme}'.`);\n                messageContainer.setAttribute('style', 'white-space: pre-line');\n                const details = document.createElement('p');\n                details.textContent = 'Administrator privileges are required, you will need to enter your password next.';\n                messageContainer.appendChild(details);\n                const dialog = new ConfirmDialog({\n                    title: nls.localizeByDefault('Create launcher'),\n                    msg: messageContainer,\n                    ok: Dialog.YES,\n                    cancel: Dialog.NO\n                });\n                const install = await dialog.open();\n                this.launcherService.createLauncher(!!install, uriScheme);\n                this.logger.info('Initialized application launcher.');\n            } else {\n                this.logger.info('Application launcher was already initialized.');\n            }\n        });\n\n        this.desktopFileService.isInitialized().then(async initialized => {\n            if (!initialized) {\n                const messageContainer = document.createElement('div');\n                // eslint-disable-next-line max-len\n                messageContainer.textContent = nls.localizeByDefault(`Would you like to create a .desktop file for ${applicationName}?\\nThis will make it easier to open ${applicationName} directly\\nfrom your applications menu and enables further features.`);\n                messageContainer.setAttribute('style', 'white-space: pre-line');\n                const dialog = new ConfirmDialog({\n                    title: nls.localizeByDefault('Create .desktop file'),\n                    msg: messageContainer,\n                    ok: Dialog.YES,\n                    cancel: Dialog.NO\n                });\n                const install = await dialog.open();\n                this.desktopFileService.createOrUpdateDesktopfile(!!install, {\n                    applicationName,\n                    createUrlHandler: true,\n                    uriScheme\n                });\n                this.logger.info('Created or updated .desktop file.');\n            } else {\n                this.logger.info('Desktop file was not updated or created.');\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "theia-extensions/launcher/src/browser/create-launcher-frontend-module.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2022-2024 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\nimport { CreateLauncherCommandContribution } from './create-launcher-contribution';\nimport { ContainerModule } from '@theia/core/shared/inversify';\nimport { LauncherService } from './launcher-service';\nimport { FrontendApplicationContribution } from '@theia/core/lib/browser';\nimport { DesktopFileService } from './desktopfile-service';\n\nexport default new ContainerModule(bind => {\n    bind(FrontendApplicationContribution).to(CreateLauncherCommandContribution);\n    bind(LauncherService).toSelf().inSingletonScope();\n    bind(DesktopFileService).toSelf().inSingletonScope();\n});\n"
  },
  {
    "path": "theia-extensions/launcher/src/browser/desktopfile-service.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2024 STMicroelectronics and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { Endpoint } from '@theia/core/lib/browser';\nimport { injectable } from '@theia/core/shared/inversify';\n\nexport interface DesktopFileOptions {\n    applicationName?: string;\n    createUrlHandler?: boolean;\n    uriScheme?: string;\n}\n\n@injectable()\nexport class DesktopFileService {\n\n    async isInitialized(): Promise<boolean> {\n        const response = await fetch(new Request(`${this.endpoint()}/initialized`), {\n            body: undefined,\n            method: 'GET'\n        }).then(r => r.json());\n        return !!response?.initialized;\n    }\n\n    async createOrUpdateDesktopfile(create: boolean, options?: DesktopFileOptions): Promise<void> {\n        fetch(new Request(`${this.endpoint()}`), {\n            body: JSON.stringify({ create, ...options }),\n            method: 'PUT',\n            headers: new Headers({ 'Content-Type': 'application/json' })\n        });\n    }\n\n    protected endpoint(): string {\n        const url = new Endpoint({ path: 'desktopfile' }).getRestUrl().toString();\n        return url.endsWith('/') ? url.slice(0, -1) : url;\n    }\n}\n"
  },
  {
    "path": "theia-extensions/launcher/src/browser/launcher-service.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2022 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { Endpoint } from '@theia/core/lib/browser';\nimport { injectable } from '@theia/core/shared/inversify';\n\n@injectable()\nexport class LauncherService {\n\n    async isInitialized(uriScheme?: string): Promise<boolean> {\n        const query = uriScheme ? `?uriScheme=${encodeURIComponent(uriScheme)}` : '';\n        const response = await fetch(new Request(`${this.endpoint()}/initialized${query}`), {\n            body: undefined,\n            method: 'GET'\n        }).then(r => r.json());\n        return !!response?.initialized;\n    }\n\n    async createLauncher(create: boolean, uriScheme?: string): Promise<void> {\n        fetch(new Request(`${this.endpoint()}`), {\n            body: JSON.stringify({ create, uriScheme }),\n            method: 'PUT',\n            headers: new Headers({ 'Content-Type': 'application/json' })\n        });\n    }\n\n    protected endpoint(): string {\n        const url = new Endpoint({ path: 'launcher' }).getRestUrl().toString();\n        return url.endsWith('/') ? url.slice(0, -1) : url;\n    }\n}\n"
  },
  {
    "path": "theia-extensions/launcher/src/node/desktopfile-endpoint.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2024 STMicroelectronics and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';\nimport { Application, Router } from '@theia/core/shared/express';\nimport { inject, injectable } from '@theia/core/shared/inversify';\nimport { Request, Response } from 'express-serve-static-core';\nimport { json } from 'body-parser';\nimport { EnvVariablesServer } from '@theia/core/lib/common/env-variables';\nimport { getStorageFilePath } from './launcher-util';\nimport * as fs from 'fs-extra';\nimport * as path from 'path';\n\ninterface DesktopFileInformation {\n    appImage: string;\n    declined: string[];\n}\n\n@injectable()\nexport class TheiaDesktopFileServiceEndpoint implements BackendApplicationContribution {\n\n    protected static PATH = '/desktopfile';\n    protected static STORAGE_FILE_NAME = 'desktopfile.json';\n\n    @inject(EnvVariablesServer)\n    protected readonly envServer: EnvVariablesServer;\n\n    configure(app: Application): void {\n        const router = Router();\n        router.put('/', (request, response) => this.createOrUpdateDesktopfile(request, response));\n        router.get('/initialized', (request, response) => this.isInitialized(request, response));\n        app.use(json());\n        app.use(TheiaDesktopFileServiceEndpoint.PATH, router);\n    }\n\n    protected async isInitialized(_request: Request, response: Response): Promise<void> {\n        if (!process.env.APPIMAGE) {\n            // we only want to create Desktop Files when running as an App Image\n            response.json({ initialized: true });\n        }\n        if (process.env.HOME === undefined) {\n            // log error but assume initialized, since we can't proceed\n            console.error('Desktop files can only be created if there is a set HOME directory');\n            response.json({ initialized: true });\n        }\n        const storageFile = await getStorageFilePath(this.envServer, TheiaDesktopFileServiceEndpoint.STORAGE_FILE_NAME);\n        if (!storageFile) {\n            throw new Error('Could not resolve path to storage file.');\n        }\n        if (!fs.existsSync(storageFile)) {\n            response.json({ initialized: false });\n            return;\n        }\n        const appImageInformation = await this.readAppImageInformationFromStorage(storageFile);\n        if (appImageInformation === undefined) {\n            response.json({ initialized: false });\n            return;\n        }\n        if (appImageInformation.declined !== undefined && appImageInformation.declined.includes(process.env.APPIMAGE!)) {\n            // we don't want to create Desktop Files for this App Image\n            response.json({ initialized: true });\n            return;\n        }\n        const initialized = appImageInformation.appImage === process.env.APPIMAGE;\n        response.json({ initialized });\n    }\n\n    protected async readAppImageInformationFromStorage(storageFile: string): Promise<DesktopFileInformation | undefined> {\n        if (!fs.existsSync(storageFile)) {\n            return undefined;\n        }\n        try {\n            const data: DesktopFileInformation = await fs.readJSON(storageFile);\n            return data;\n        } catch (error) {\n            console.error('Failed to parse data from \"', storageFile, '\". Reason:', error);\n            return undefined;\n        }\n    }\n\n    protected async createOrUpdateDesktopfile(request: Request, response: Response): Promise<void> {\n        const storageFile = await getStorageFilePath(this.envServer, TheiaDesktopFileServiceEndpoint.STORAGE_FILE_NAME);\n        let appImageInformation: DesktopFileInformation | undefined = await this.readAppImageInformationFromStorage(storageFile);\n        if (appImageInformation === undefined) {\n            appImageInformation = { appImage: '', declined: [] };\n        }\n\n        const createOrUpdate = request.body.create;\n        const applicationName: string = request.body.applicationName || 'Theia IDE';\n        const createUrlHandler: boolean = request.body.createUrlHandler !== false;\n        const uriScheme: string = request.body.uriScheme || 'theia';\n        const appId = applicationName.toLowerCase().replace(/\\s+/g, '-');\n\n        if (createOrUpdate) {\n            const iconFileName = appId + '-electron-app.png';\n            const applicationsDir = path.join(process.env.HOME!, '.local', 'share', 'applications');\n            const imagePath = path.join(applicationsDir, iconFileName);\n            if (!fs.existsSync(imagePath)) {\n                const appDir = process.env.APPDIR;\n                if (appDir !== undefined) {\n                    let unpackedImagePath = path.join(appDir, iconFileName);\n                    if (!fs.existsSync(unpackedImagePath)) {\n                        // Fallback: find any .png icon in the AppImage root\n                        try {\n                            const pngFile = fs.readdirSync(appDir).find((f: string) => f.endsWith('.png'));\n                            if (pngFile) {\n                                unpackedImagePath = path.join(appDir, pngFile);\n                            }\n                        } catch { /* ignore */ }\n                    }\n                    if (fs.existsSync(unpackedImagePath)) {\n                        fs.copyFileSync(unpackedImagePath, imagePath);\n                    } else {\n                        console.warn('Launcher Icon not Found in App Image');\n                    }\n                } else {\n                    console.warn('Path for unpacked App Image not found');\n                }\n            }\n\n            const desktopFilePath = path.join(applicationsDir, `${appId}-launcher.desktop`);\n            fs.outputFileSync(desktopFilePath, this.getDesktopFileContents(applicationName, process.env.APPIMAGE!, imagePath));\n\n            if (createUrlHandler) {\n                const desktopURLFilePath = path.join(applicationsDir, `${appId}-launcher-url.desktop`);\n                fs.outputFileSync(desktopURLFilePath, this.getDesktopURLFileContents(applicationName, process.env.APPIMAGE!, imagePath, uriScheme));\n            }\n\n            appImageInformation.appImage = process.env.APPIMAGE!;\n            fs.outputJSONSync(storageFile, appImageInformation);\n        } else {\n            appImageInformation.declined.push(process.env.APPIMAGE!);\n            fs.outputJSONSync(storageFile, appImageInformation);\n        }\n\n        response.sendStatus(200);\n    }\n\n    protected getDesktopFileContents(applicationName: string, appImagePath: string, imagePath: string): string {\n        return `[Desktop Entry]\nName=${applicationName}\nGenericName=Integrated Development Environment\nExec=${appImagePath} %U\nTerminal=false\nType=Application\nIcon=${imagePath}\nStartupWMClass=${applicationName}\nComment=IDE for cloud and desktop\nCategories=Development;IDE;`;\n    }\n\n    protected getDesktopURLFileContents(applicationName: string, appImagePath: string, imagePath: string, uriScheme: string = 'theia'): string {\n        return `[Desktop Entry]\nName=${applicationName} - URL Handler\nGenericName=Integrated Development Environment\nExec=${appImagePath} --open-url %U\nTerminal=false\nType=Application\nNoDisplay=true\nIcon=${imagePath}\nMimeType=x-scheme-handler/${uriScheme};\nComment=IDE for cloud and desktop\nCategories=Development;IDE;`;\n    }\n}\n"
  },
  {
    "path": "theia-extensions/launcher/src/node/launcher-backend-module.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2022-2024 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { ContainerModule } from '@theia/core/shared/inversify';\nimport { TheiaLauncherServiceEndpoint } from './launcher-endpoint';\nimport { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';\nimport { TheiaDesktopFileServiceEndpoint } from './desktopfile-endpoint';\n\nexport default new ContainerModule(bind => {\n    bind(TheiaLauncherServiceEndpoint).toSelf().inSingletonScope();\n    bind(BackendApplicationContribution).toService(TheiaLauncherServiceEndpoint);\n\n    bind(TheiaDesktopFileServiceEndpoint).toSelf().inSingletonScope();\n    bind(BackendApplicationContribution).toService(TheiaDesktopFileServiceEndpoint);\n});\n"
  },
  {
    "path": "theia-extensions/launcher/src/node/launcher-endpoint.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2022-2024 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { inject, injectable } from '@theia/core/shared/inversify';\nimport { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';\nimport { Application, Router, Request, Response } from '@theia/core/shared/express';\nimport { json } from 'body-parser';\nimport { ILogger } from '@theia/core/lib/common';\nimport { EnvVariablesServer } from '@theia/core/lib/common/env-variables';\nimport * as sudo from '@vscode/sudo-prompt';\nimport * as fs from 'fs-extra';\nimport URI from '@theia/core/lib/common/uri';\nimport { getStorageFilePath } from './launcher-util';\n\ninterface PathEntry {\n    source: string;\n    target: string;\n}\n\n@injectable()\nexport class TheiaLauncherServiceEndpoint implements BackendApplicationContribution {\n    protected static PATH = '/launcher';\n    protected static STORAGE_FILE_NAME = 'paths.json';\n    @inject(ILogger)\n    protected readonly logger: ILogger;\n\n    @inject(EnvVariablesServer)\n    protected readonly envServer: EnvVariablesServer;\n\n    configure(app: Application): void {\n        const router = Router();\n        router.put('/', (request, response) => this.createLauncher(request, response));\n        router.get('/initialized', (request, response) => this.isInitialized(request, response));\n        app.use(json());\n        app.use(TheiaLauncherServiceEndpoint.PATH, router);\n    }\n\n    private async isInitialized(request: Request, response: Response): Promise<void> {\n        if (!process.env.APPIMAGE) {\n            // we are not running from an AppImage, so there's nothing to initialize\n            // return true\n            response.json({ initialized: true });\n        }\n        const uriScheme = (request.query.uriScheme as string) || 'theia';\n        const launcherLink = `/usr/local/bin/${uriScheme}`;\n        const storageFile = await getStorageFilePath(this.envServer, TheiaLauncherServiceEndpoint.STORAGE_FILE_NAME);\n        if (!storageFile) {\n            throw new Error('Could not resolve path to storage file.');\n        }\n        if (!fs.existsSync(storageFile)) {\n            response.json({ initialized: false });\n            return;\n        }\n        const data = await this.readLauncherPathsFromStorage(storageFile);\n        const initialized = !!data.find(entry => entry.source === launcherLink);\n        response.json({ initialized });\n    }\n\n    private async readLauncherPathsFromStorage(storageFile: string): Promise<PathEntry[]> {\n        if (!fs.existsSync(storageFile)) {\n            return [];\n        }\n        try {\n            return await fs.readJSON(storageFile);\n        } catch (error) {\n            console.error('Failed to parse data from \"', storageFile, '\". Reason:', error);\n            return [];\n        }\n    }\n\n    private async getLogFilePath(): Promise<string> {\n        const configDirUri = await this.envServer.getConfigDirUri();\n        const logFileUri = new URI(configDirUri).resolve('logs/launcher.log');\n        return logFileUri.path.fsPath();\n    }\n\n    private async createLauncher(request: Request, response: Response): Promise<void> {\n        const shouldCreateLauncher = request.body.create;\n        const uriScheme: string = request.body.uriScheme || 'theia';\n        const launcher = `/usr/local/bin/${uriScheme}`;\n        const sudoPromptName = uriScheme === 'theia-next' ? 'Theia IDE Next' : 'Theia IDE';\n        const target = process.env.APPIMAGE;\n        const logFile = await this.getLogFilePath();\n        const command = `printf '%s\\n' '#!/bin/bash' 'exec \"${target}\" \\\\$1 &> ${logFile} &' >${launcher} && chmod +x ${launcher}`;\n        if (shouldCreateLauncher) {\n            const targetExists = target && fs.existsSync(target);\n            if (!targetExists) {\n                throw new Error('Could not find application to launch');\n            }\n            sudo.exec(command, { name: sudoPromptName });\n        }\n\n        const storageFile = await getStorageFilePath(this.envServer, TheiaLauncherServiceEndpoint.STORAGE_FILE_NAME);\n        const data = fs.existsSync(storageFile) ? await this.readLauncherPathsFromStorage(storageFile) : [];\n        fs.outputJSONSync(storageFile, [...data, { source: launcher, target: shouldCreateLauncher ? target : undefined }]);\n\n        response.sendStatus(200);\n    }\n}\n"
  },
  {
    "path": "theia-extensions/launcher/src/node/launcher-util.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2024 STMicroelectronics and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { EnvVariablesServer } from '@theia/core/lib/common/env-variables';\nimport URI from '@theia/core/lib/common/uri';\n\nexport async function getStorageFilePath(envServer: EnvVariablesServer, fileName: string): Promise<string> {\n    const configDirUri = await envServer.getConfigDirUri();\n    const globalStorageFolderUri = new URI(configDirUri).resolve('globalStorage/theia-ide-launcher/' + fileName);\n    const globalStorageFolderFsPath = globalStorageFolderUri.path.fsPath();\n    return globalStorageFolderFsPath;\n}\n"
  },
  {
    "path": "theia-extensions/launcher/tsconfig.json",
    "content": "{\n  \"extends\": \"../../configs/base.tsconfig\",\n  \"compilerOptions\": {\n    \"rootDir\": \"src\",\n    \"outDir\": \"lib\",\n    \"baseUrl\": \".\",\n    \"esModuleInterop\": true\n  },\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "theia-extensions/product/.eslintrc.js",
    "content": "/** @type {import('eslint').Linter.Config} */\nmodule.exports = {\n    extends: [\n        '../../configs/build.eslintrc.json'\n    ],\n    parserOptions: {\n        tsconfigRootDir: __dirname,\n        project: 'tsconfig.json'\n    }\n};\n"
  },
  {
    "path": "theia-extensions/product/package.json",
    "content": "{\n  \"private\": true,\n  \"name\": \"theia-ide-product-ext\",\n  \"version\": \"1.71.100\",\n  \"description\": \"Eclipse Theia IDE Product Branding\",\n  \"dependencies\": {\n    \"@theia/core\": \"1.72.0-next.20\",\n    \"@theia/getting-started\": \"1.72.0-next.20\",\n    \"@theia/vsx-registry\": \"1.72.0-next.20\",\n    \"@theia/workspace\": \"1.72.0-next.20\",\n    \"inversify\": \"^6.2.2\"\n  },\n  \"devDependencies\": {\n    \"rimraf\": \"^2.7.1\",\n    \"tslint\": \"^5.20.1\",\n    \"typescript\": \"^4.9.5\"\n  },\n  \"theiaExtensions\": [\n    {\n      \"frontend\": \"lib/browser/theia-ide-frontend-module\",\n      \"electronMain\": \"lib/electron-main/theia-ide-main-module\"\n    }\n  ],\n  \"keywords\": [\n    \"theia-extension\"\n  ],\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/eclipse-theia/theia-ide.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/eclipse-theia/theia-ide/issues\"\n  },\n  \"homepage\": \"https://github.com/eclipse-theia/theia-ide\",\n  \"files\": [\n    \"lib\",\n    \"src\"\n  ],\n  \"scripts\": {\n    \"clean\": \"rimraf lib *.tsbuildinfo\",\n    \"build\": \"tsc -b\",\n    \"lint\": \"eslint --ext js,jsx,ts,tsx src\",\n    \"lint:fix\": \"eslint --ext js,jsx,ts,tsx src --fix\",\n    \"update:theia\": \"ts-node ../../scripts/update-theia-version.ts\",\n    \"update:next\": \"ts-node ../../scripts/update-theia-version.ts next\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"^16.8.0\"\n  }\n}"
  },
  {
    "path": "theia-extensions/product/src/browser/branding-util.tsx",
    "content": "/********************************************************************************\n * Copyright (C) 2020 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { WindowService } from '@theia/core/lib/browser/window/window-service';\nimport * as React from 'react';\nimport { getBrandingVariant } from './theia-ide-config';\n\nexport interface ExternalBrowserLinkProps {\n    text: string;\n    url: string;\n    windowService: WindowService;\n}\n\nexport function renderProductName(): React.ReactNode {\n    const variant = getBrandingVariant();\n    const suffix = variant !== 'stable' ? ` ${variant.charAt(0).toUpperCase() + variant.slice(1)}` : '';\n    return <h1>Eclipse Theia <span className=\"gs-blue-header\">IDE</span>{suffix}</h1>;\n}\n\nfunction BrowserLink(props: ExternalBrowserLinkProps): JSX.Element {\n    return <a\n        role={'button'}\n        tabIndex={0}\n        href={props.url}\n        target='_blank'\n    >\n        {props.text}\n    </a>;\n}\n\nexport function renderWhatIs(windowService: WindowService): React.ReactNode {\n    return <div className='gs-section'>\n        <h3 className='gs-section-header'>\n            What is this?\n        </h3>\n        <div>\n            The Eclipse Theia IDE is a modern and open IDE for cloud and desktop. The Theia IDE is based on the <BrowserLink text=\"Theia platform\"\n                url=\"https://theia-ide.org\" windowService={windowService} ></BrowserLink>.\n        </div>\n        <div>\n            The IDE is available as a <BrowserLink text=\"downloadable desktop application\" url=\"https://theia-ide.org//#theiaidedownload\"\n                windowService={windowService} ></BrowserLink>. You can also <BrowserLink text=\"try the latest version of the Theia IDE online\"\n                    url=\"https://try.theia-cloud.io/\" windowService={windowService} ></BrowserLink>. The online test version is limited to 30 minutes per session and hosted\n            via <BrowserLink text=\"Theia Cloud\" url=\"https://theia-cloud.io/\" windowService={windowService} ></BrowserLink>.\n        </div>\n    </div>;\n}\n\nexport function renderExtendingCustomizing(windowService: WindowService): React.ReactNode {\n    return <div className='gs-section'>\n        <h3 className='gs-section-header'>\n            Extending/Customizing the Theia IDE\n        </h3>\n        <div >\n            You can extend the Theia IDE at runtime by installing VS Code extensions, e.g. from the <BrowserLink text=\"OpenVSX registry\" url=\"https://open-vsx.org/\"\n                windowService={windowService} ></BrowserLink>, an open marketplace for VS Code extensions. Just open the extension view or browse <BrowserLink\n                    text=\"OpenVSX online\" url=\"https://open-vsx.org/\" windowService={windowService} ></BrowserLink>.\n        </div>\n        <div>\n            Furthermore, the Theia IDE is based on the flexible Theia platform. Therefore, the Theia IDE can serve as a <span className='gs-text-bold'>template</span> for building\n            custom tools and IDEs. Browse <BrowserLink text=\"the documentation\" url=\"https://theia-ide.org/docs/composing_applications/\"\n                windowService={windowService} ></BrowserLink> to help you customize and build your own Eclipse Theia-based product.\n        </div>\n    </div>;\n}\n\nexport function renderSupport(windowService: WindowService): React.ReactNode {\n    return <div className='gs-section'>\n        <h3 className='gs-section-header'>\n            Professional Support\n        </h3>\n        <div>\n            Professional support, implementation services, consulting and training for building tools like Theia IDE and for building other tools based on Eclipse Theia is\n            available by selected companies as listed on the <BrowserLink text=\" Theia support page\" url=\"https://theia-ide.org/support/\"\n                windowService={windowService} ></BrowserLink>.\n        </div>\n    </div>;\n}\n\nexport function renderTickets(windowService: WindowService): React.ReactNode {\n    return <div className='gs-section'>\n        <h3 className='gs-section-header'>\n            Reporting feature requests and bugs\n        </h3>\n        <div >\n            The features in the Eclipse Theia IDE are based on Theia and the included\n            extensions/plugins. For bugs in Theia please consider opening an issue in\n            the <BrowserLink text=\"Theia project on Github\" url=\"https://github.com/eclipse-theia/theia/issues/new/choose\"\n                windowService={windowService} ></BrowserLink>.\n        </div>\n        <div>\n            Eclipse Theia IDE only packages existing functionality into a product and installers\n            for the product. If you believe there is a mistake in packaging, something needs to be added to the\n            packaging or the installers do not work properly,\n            please <BrowserLink text=\"open an issue on Github\" url=\"https://github.com/eclipse-theia/theia-ide/issues/new/choose\"\n                windowService={windowService} ></BrowserLink> to let us know.\n        </div>\n    </div>;\n}\n\nexport function renderSourceCode(windowService: WindowService): React.ReactNode {\n    return <div className='gs-section'>\n        <h3 className='gs-section-header'>\n            Source Code\n        </h3>\n        <div >\n            The source code of Eclipse Theia IDE is available\n            on <BrowserLink text=\"Github\" url=\"https://github.com/eclipse-theia/theia-ide\"\n                windowService={windowService} ></BrowserLink>.\n        </div>\n    </div>;\n}\n\nexport function renderDocumentation(windowService: WindowService): React.ReactNode {\n    return <div className='gs-section'>\n        <h3 className='gs-section-header'>\n            Documentation\n        </h3>\n        <div >\n            Please see the <BrowserLink text=\"documentation\" url=\"https://theia-ide.org/docs/user_getting_started/\"\n                windowService={windowService} ></BrowserLink> on how to use the Theia IDE.\n        </div>\n    </div>;\n}\n\nexport function renderCollaboration(windowService: WindowService): React.ReactNode {\n    return <div className='gs-section'>\n        <h3 className='gs-section-header'>\n            Collaboration\n        </h3>\n        <div >\n            The IDE features a built-in collaboration feature.\n            You can share your workspace with others and work together in real-time by clicking on the <i>Collaborate</i> item in the status bar.\n            The collaboration feature is powered by\n            the <BrowserLink text=\"Open Collaboration Tools\" url=\"https://www.open-collab.tools/\" windowService={windowService} /> project\n            and uses their public server infrastructure.\n        </div>\n    </div>;\n}\n\nexport function renderDownloads(): React.ReactNode {\n    return <div className='gs-section'>\n        <h3 className='gs-section-header'>\n            Updates and Downloads\n        </h3>\n        <div className='gs-action-container'>\n            You can update Eclipse Theia IDE directly in this application by navigating to\n            File {'>'} Preferences {'>'} Check for Updates… Moreover the application will check for updates\n            after each launch automatically.\n        </div>\n        <div className='gs-action-container'>\n            Alternatively you can download the most recent version from the download page.\n        </div>\n    </div>;\n}\n"
  },
  {
    "path": "theia-extensions/product/src/browser/style/index.css",
    "content": "/********************************************************************************\n * Copyright (C) 2020 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\n:root {\n    --theia-branding-logo: url(../icons/TheiaIDE.png);\n}\n\n.theia-icon {\n    background-image: url(\"../icons/512-512.png\");\n    background-position: center;\n    background-repeat: no-repeat;\n    background-size: contain;\n}\n\nbody[data-theia-branding=\"next\"] {\n    --theia-branding-logo: url(../icons/TheiaIDE-next.png);\n}\n\nbody[data-theia-branding=\"next\"] .theia-icon {\n    background-image: url(\"../icons/512-512-next.png\");\n}\n\n.gs-blue-header {\n    color: #5088e7;\n    text-transform: capitalize;\n    font-weight: 600;\n}\n\n.gs-text-bold {\n    font-weight: 600;\n}\n\n.gs-text-underline {\n    text-decoration: underline;\n}\n\n.gs-float {\n    float: right;\n    padding-left: 20px;\n}\n\n.gs-logo {\n    background-image: var(--theia-branding-logo);\n    background-position: center center;\n    background-repeat: no-repeat;\n    background-size: contain;\n    width: 250px;\n    height: 118px;\n    padding: 20px;\n}\n\n.ad-logo {\n    background-image: var(--theia-branding-logo);\n    background-position: center center;\n    background-repeat: no-repeat;\n    background-size: contain;\n    width: 250px;\n    height: 118px;\n    padding: 20px;\n}\n\n.ad-float {\n    float: right;\n}\n\n.ad-container {\n    padding: 20px;\n    width: 1150px;\n    height: 700;\n}\n\nul.theia-aboutExtensions {\n    height: 450px;\n    overflow: hidden;\n    overflow-y: scroll;\n    list-style-type: none;\n    padding: 0;\n    margin-left: 10px;\n}"
  },
  {
    "path": "theia-extensions/product/src/browser/theia-ide-about-dialog.tsx",
    "content": "/********************************************************************************\n * Copyright (C) 2020 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport * as React from 'react';\nimport { AboutDialog, AboutDialogProps, ABOUT_CONTENT_CLASS } from '@theia/core/lib/browser/about-dialog';\nimport { injectable, inject } from '@theia/core/shared/inversify';\nimport { renderDocumentation, renderDownloads, renderProductName, renderSourceCode, renderSupport, renderTickets, renderWhatIs } from './branding-util';\nimport { VSXEnvironment } from '@theia/vsx-registry/lib/common/vsx-environment';\nimport { WindowService } from '@theia/core/lib/browser/window/window-service';\n@injectable()\nexport class TheiaIDEAboutDialog extends AboutDialog {\n\n    @inject(VSXEnvironment)\n    protected readonly environment: VSXEnvironment;\n\n    @inject(WindowService)\n    protected readonly windowService: WindowService;\n\n    protected vscodeApiVersion: string;\n\n    constructor(\n        @inject(AboutDialogProps) protected readonly props: AboutDialogProps\n    ) {\n        super(props);\n    }\n\n    protected async doInit(): Promise<void> {\n        this.vscodeApiVersion = await this.environment.getVscodeApiVersion();\n        super.doInit();\n    }\n\n    protected render(): React.ReactNode {\n        return <div className={ABOUT_CONTENT_CLASS}>\n            {this.renderContent()}\n        </div>;\n    }\n\n    protected renderContent(): React.ReactNode {\n        return <div className='ad-container'>\n            <div className='ad-float'>\n                <div className='ad-logo'>\n                </div>\n                {this.renderExtensions()}\n            </div>\n            {this.renderTitle()}\n            <hr className='gs-hr' />\n            <div className='flex-grid'>\n                <div className='col'>\n                    {renderWhatIs(this.windowService)}\n                </div>\n            </div>\n            <div className='flex-grid'>\n                <div className='col'>\n                    {renderSupport(this.windowService)}\n                </div>\n            </div>\n            <div className='flex-grid'>\n                <div className='col'>\n                    {renderTickets(this.windowService)}\n                </div>\n            </div>\n            <div className='flex-grid'>\n                <div className='col'>\n                    {renderSourceCode(this.windowService)}\n                </div>\n            </div>\n            <div className='flex-grid'>\n                <div className='col'>\n                    {renderDocumentation(this.windowService)}\n                </div>\n            </div>\n            <div className='flex-grid'>\n                <div className='col'>\n                    {renderDownloads()}\n                </div>\n            </div>\n        </div>;\n\n    }\n\n    protected renderTitle(): React.ReactNode {\n        return <div className='gs-header'>\n            {renderProductName()}\n            {this.renderVersion()}\n        </div>;\n    }\n\n    protected renderVersion(): React.ReactNode {\n        return <div>\n            <p className='gs-sub-header' >\n                {this.applicationInfo ? 'Version ' + this.applicationInfo.version : '-'}\n            </p>\n\n            <p className='gs-sub-header' >\n                {'VS Code API Version: ' + this.vscodeApiVersion}\n            </p>\n        </div>;\n    }\n}\n"
  },
  {
    "path": "theia-extensions/product/src/browser/theia-ide-config.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2026 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';\n\nexport type BrandingVariant = 'stable' | 'next';\n\nexport function getBrandingVariant(): BrandingVariant {\n    try {\n        const config = FrontendApplicationConfigProvider.get() as Record<string, unknown>;\n        return (config['brandingVariant'] as BrandingVariant) ?? 'stable';\n    } catch {\n        return 'stable';\n    }\n}\n\nexport function applyBranding(): void {\n    const variant = getBrandingVariant();\n    if (variant !== 'stable') {\n        document.body.setAttribute('data-theia-branding', variant);\n    }\n}\n"
  },
  {
    "path": "theia-extensions/product/src/browser/theia-ide-contribution.tsx",
    "content": "/********************************************************************************\n * Copyright (C) 2021 Ericsson and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { inject, injectable } from '@theia/core/shared/inversify';\nimport { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution';\nimport { Command, CommandContribution, CommandRegistry } from '@theia/core/lib/common/command';\nimport { MenuContribution, MenuModelRegistry, MenuPath } from '@theia/core/lib/common/menu';\nimport { WindowService } from '@theia/core/lib/browser/window/window-service';\n\nexport namespace TheiaIDEMenus {\n    export const THEIA_IDE_HELP: MenuPath = [...CommonMenus.HELP, 'theia-ide'];\n}\nexport namespace TheiaIDECommands {\n    export const CATEGORY = 'TheiaIDE';\n    export const REPORT_ISSUE: Command = {\n        id: 'theia-ide:report-issue',\n        category: CATEGORY,\n        label: 'Report Issue'\n    };\n    export const DOCUMENTATION: Command = {\n        id: 'theia-ide:documentation',\n        category: CATEGORY,\n        label: 'Documentation'\n    };\n}\n\n@injectable()\nexport class TheiaIDEContribution implements CommandContribution, MenuContribution {\n\n    @inject(WindowService)\n    protected readonly windowService: WindowService;\n\n    static REPORT_ISSUE_URL = 'https://github.com/eclipse-theia/theia-ide/issues/new?assignees=&labels=&template=bug_report.md';\n    static DOCUMENTATION_URL = 'https://theia-ide.org/docs/user_getting_started/';\n\n    registerCommands(commandRegistry: CommandRegistry): void {\n        commandRegistry.registerCommand(TheiaIDECommands.REPORT_ISSUE, {\n            execute: () => this.windowService.openNewWindow(TheiaIDEContribution.REPORT_ISSUE_URL, { external: true })\n        });\n        commandRegistry.registerCommand(TheiaIDECommands.DOCUMENTATION, {\n            execute: () => this.windowService.openNewWindow(TheiaIDEContribution.DOCUMENTATION_URL, { external: true })\n        });\n    }\n\n    registerMenus(menus: MenuModelRegistry): void {\n        menus.registerMenuAction(TheiaIDEMenus.THEIA_IDE_HELP, {\n            commandId: TheiaIDECommands.REPORT_ISSUE.id,\n            label: TheiaIDECommands.REPORT_ISSUE.label,\n            order: '1'\n        });\n        menus.registerMenuAction(TheiaIDEMenus.THEIA_IDE_HELP, {\n            commandId: TheiaIDECommands.DOCUMENTATION.id,\n            label: TheiaIDECommands.DOCUMENTATION.label,\n            order: '2'\n        });\n    }\n}\n"
  },
  {
    "path": "theia-extensions/product/src/browser/theia-ide-frontend-module.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2020 TypeFox, EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport '../../src/browser/style/index.css';\n\nimport { WidgetFactory } from '@theia/core/lib/browser';\nimport { AboutDialog } from '@theia/core/lib/browser/about-dialog';\nimport { applyBranding } from './theia-ide-config';\nimport { CommandContribution } from '@theia/core/lib/common/command';\nimport { ContainerModule } from '@theia/core/shared/inversify';\nimport { GettingStartedWidget } from '@theia/getting-started/lib/browser/getting-started-widget';\nimport { MenuContribution } from '@theia/core/lib/common/menu';\nimport { TheiaIDEAboutDialog } from './theia-ide-about-dialog';\nimport { TheiaIDEContribution } from './theia-ide-contribution';\nimport { TheiaIDEGettingStartedWidget } from './theia-ide-getting-started-widget';\n\nexport default new ContainerModule((bind, _unbind, isBound, rebind) => {\n    applyBranding();\n\n    bind(TheiaIDEGettingStartedWidget).toSelf();\n    bind(WidgetFactory).toDynamicValue(context => ({\n        id: GettingStartedWidget.ID,\n        createWidget: () => context.container.get<TheiaIDEGettingStartedWidget>(TheiaIDEGettingStartedWidget),\n    })).inSingletonScope();\n    if (isBound(AboutDialog)) {\n        rebind(AboutDialog).to(TheiaIDEAboutDialog).inSingletonScope();\n    } else {\n        bind(AboutDialog).to(TheiaIDEAboutDialog).inSingletonScope();\n    }\n\n    bind(TheiaIDEContribution).toSelf().inSingletonScope();\n    [CommandContribution, MenuContribution].forEach(serviceIdentifier =>\n        bind(serviceIdentifier).toService(TheiaIDEContribution)\n    );\n});\n"
  },
  {
    "path": "theia-extensions/product/src/browser/theia-ide-getting-started-widget.tsx",
    "content": "/********************************************************************************\n * Copyright (C) 2020 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport * as React from 'react';\n\nimport { Message } from '@theia/core/lib/browser';\nimport { PreferenceService } from '@theia/core/lib/common';\nimport { inject, injectable } from '@theia/core/shared/inversify';\nimport {\n    renderDocumentation, renderDownloads, renderExtendingCustomizing, renderProductName, renderSourceCode, renderSupport, renderTickets, renderWhatIs, renderCollaboration\n} from './branding-util';\n\nimport { GettingStartedWidget } from '@theia/getting-started/lib/browser/getting-started-widget';\nimport { VSXEnvironment } from '@theia/vsx-registry/lib/common/vsx-environment';\nimport { WindowService } from '@theia/core/lib/browser/window/window-service';\n\n@injectable()\nexport class TheiaIDEGettingStartedWidget extends GettingStartedWidget {\n\n    @inject(VSXEnvironment)\n    protected readonly environment: VSXEnvironment;\n\n    @inject(WindowService)\n    protected readonly windowService: WindowService;\n\n    @inject(PreferenceService)\n    protected readonly preferenceService: PreferenceService;\n\n    protected vscodeApiVersion: string;\n\n    protected async doInit(): Promise<void> {\n        super.doInit();\n        this.vscodeApiVersion = await this.environment.getVscodeApiVersion();\n        await this.preferenceService.ready;\n        this.update();\n    }\n\n    protected onActivateRequest(msg: Message): void {\n        super.onActivateRequest(msg);\n        const htmlElement = document.getElementById('alwaysShowWelcomePage');\n        if (htmlElement) {\n            htmlElement.focus();\n        }\n    }\n\n    protected render(): React.ReactNode {\n        return <div className='gs-container'>\n            <div className='gs-content-container'>\n                <div className='gs-float'>\n                    <div className='gs-logo'>\n                    </div>\n                    {this.renderActions()}\n                </div>\n                {this.renderHeader()}\n                <hr className='gs-hr' />\n                <div className='flex-grid'>\n                    <div className='col'>\n                        {this.renderNews()}\n                    </div>\n                </div>\n                <div className='flex-grid'>\n                    <div className='col'>\n                        {renderWhatIs(this.windowService)}\n                    </div>\n                </div>\n                <div className='flex-grid'>\n                    <div className='col'>\n                        {renderExtendingCustomizing(this.windowService)}\n                    </div>\n                </div>\n                <div className='flex-grid'>\n                    <div className='col'>\n                        {renderSupport(this.windowService)}\n                    </div>\n                </div>\n                <div className='flex-grid'>\n                    <div className='col'>\n                        {renderTickets(this.windowService)}\n                    </div>\n                </div>\n                <div className='flex-grid'>\n                    <div className='col'>\n                        {renderSourceCode(this.windowService)}\n                    </div>\n                </div>\n                <div className='flex-grid'>\n                    <div className='col'>\n                        {renderDocumentation(this.windowService)}\n                    </div>\n                </div>\n                <div className='flex-grid'>\n                    <div className='col'>\n                        {this.renderAIBanner()}\n                    </div>\n                </div>\n                <div className='flex-grid'>\n                    <div className='col'>\n                        {renderCollaboration(this.windowService)}\n                    </div>\n                </div>\n                <div className='flex-grid'>\n                    <div className='col'>\n                        {renderDownloads()}\n                    </div>\n                </div>\n            </div>\n            <div className='gs-preference-container'>\n                {this.renderPreferences()}\n            </div>\n        </div>;\n    }\n\n    protected renderActions(): React.ReactNode {\n        return <div className='gs-container'>\n            <div className='flex-grid'>\n                <div className='col'>\n                    {this.renderStart()}\n                </div>\n            </div>\n            <div className='flex-grid'>\n                <div className='col'>\n                    {this.renderRecentWorkspaces()}\n                </div>\n            </div>\n            <div className='flex-grid'>\n                <div className='col'>\n                    {this.renderSettings()}\n                </div>\n            </div>\n            <div className='flex-grid'>\n                <div className='col'>\n                    {this.renderHelp()}\n                </div>\n            </div>\n        </div>;\n    }\n\n    protected renderHeader(): React.ReactNode {\n        return <div className='gs-header'>\n            {renderProductName()}\n            {this.renderVersion()}\n        </div>;\n    }\n\n    protected renderVersion(): React.ReactNode {\n        return <div>\n            <p className='gs-sub-header' >\n                {this.applicationInfo ? 'Version ' + this.applicationInfo.version : '-'}\n            </p>\n\n            <p className='gs-sub-header' >\n                {'VS Code API Version: ' + this.vscodeApiVersion}\n            </p>\n        </div>;\n    }\n\n    protected renderAIBanner(): React.ReactNode {\n        const framework = super.renderAIBanner();\n        if (React.isValidElement<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>>(framework)) {\n            return React.cloneElement(framework, { className: 'gs-section' });\n        }\n        return framework;\n    }\n}\n"
  },
  {
    "path": "theia-extensions/product/src/electron-main/icon-contribution.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2021 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport * as os from 'os';\nimport * as path from 'path';\n\nimport { ElectronMainApplication, ElectronMainApplicationContribution } from '@theia/core/lib/electron-main/electron-main-application';\n\nimport { injectable } from '@theia/core/shared/inversify';\nimport { BrowserWindow } from '@theia/core/electron-shared/electron';\n\n@injectable()\nexport class IconContribution implements ElectronMainApplicationContribution {\n\n    onStart(application: ElectronMainApplication): void {\n        if (os.platform() === 'linux') {\n            const windowOptions = application.config.electron.windowOptions;\n            if (windowOptions && windowOptions.icon === undefined) {\n                // The window image is undefined. If the executable has an image set, this is used as a fallback.\n                // Since AppImage does not support this anymore via electron-builder, set an image for the linux platform.\n                windowOptions.icon = path.join(__dirname, '../../resources/icons/WindowIcon/512-512.png');\n                // also update any existing windows, e.g. the splashscreen\n                for (const window of BrowserWindow.getAllWindows()) {\n                    window.setIcon(path.join(__dirname, '../../resources/icons/WindowIcon/512-512.png'));\n                }\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "theia-extensions/product/src/electron-main/theia-ide-main-module.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2021 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { ContainerModule } from '@theia/core/shared/inversify';\nimport { ElectronMainApplicationContribution } from '@theia/core/lib/electron-main/electron-main-application';\nimport { IconContribution } from './icon-contribution';\n\nexport default new ContainerModule(bind => {\n    bind(IconContribution).toSelf().inSingletonScope();\n    bind(ElectronMainApplicationContribution).toService(IconContribution);\n});\n"
  },
  {
    "path": "theia-extensions/product/tsconfig.json",
    "content": "{\n    \"extends\": \"../../configs/base.tsconfig\",\n    \"compilerOptions\": {\n        \"rootDir\": \"src\",\n        \"outDir\": \"lib\",\n        \"baseUrl\": \".\",\n        \"esModuleInterop\": true\n    },\n    \"include\": [\n        \"src\",\n    ]\n}\n"
  },
  {
    "path": "theia-extensions/updater/.eslintrc.js",
    "content": "/** @type {import('eslint').Linter.Config} */\nmodule.exports = {\n    extends: [\n        '../../configs/build.eslintrc.json'\n    ],\n    parserOptions: {\n        tsconfigRootDir: __dirname,\n        project: 'tsconfig.json'\n    }\n};\n"
  },
  {
    "path": "theia-extensions/updater/package.json",
    "content": "{\n  \"private\": true,\n  \"name\": \"theia-ide-updater-ext\",\n  \"version\": \"1.71.100\",\n  \"description\": \"Eclipse Theia IDE Updater\",\n  \"dependencies\": {\n    \"@theia/core\": \"1.72.0-next.20\",\n    \"@theia/output\": \"1.72.0-next.20\",\n    \"@theia/preferences\": \"1.72.0-next.20\",\n    \"builder-util-runtime\": \"9.3.1\",\n    \"electron-log\": \"^4.4.8\",\n    \"electron-updater\": \"6.6.2\",\n    \"fs-extra\": \"^10.1.0\",\n    \"vscode-uri\": \"^2.1.2\"\n  },\n  \"devDependencies\": {\n    \"rimraf\": \"^2.7.1\",\n    \"tslint\": \"^5.20.1\",\n    \"typescript\": \"^4.9.5\"\n  },\n  \"theiaExtensions\": [\n    {\n      \"electronMain\": \"lib/electron-main/update/theia-updater-main-module\",\n      \"frontendElectron\": \"lib/electron-browser/theia-updater-frontend-module\"\n    }\n  ],\n  \"keywords\": [\n    \"theia-extension\"\n  ],\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/eclipse-theia/theia-ide.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/eclipse-theia/theia-ide/issues\"\n  },\n  \"homepage\": \"https://github.com/eclipse-theia/theia-ide\",\n  \"files\": [\n    \"lib\",\n    \"src\"\n  ],\n  \"scripts\": {\n    \"clean\": \"rimraf lib *.tsbuildinfo\",\n    \"build\": \"tsc -b\",\n    \"lint\": \"eslint --ext js,jsx,ts,tsx src\",\n    \"lint:fix\": \"eslint --ext js,jsx,ts,tsx src --fix\",\n    \"update:theia\": \"ts-node ../../scripts/update-theia-version.ts\",\n    \"update:next\": \"ts-node ../../scripts/update-theia-version.ts next\"\n  }\n}"
  },
  {
    "path": "theia-extensions/updater/src/common/updater/theia-updater.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2020 TypeFox, EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { RpcServer } from '@theia/core/lib/common/messaging/proxy-factory';\n\nexport const TheiaUpdaterPath = '/services/theia-updater';\nexport const TheiaUpdater = Symbol('TheiaUpdater');\nexport interface UpdaterSettings {\n    checkForUpdates: boolean;\n    checkInterval: number;\n    channel: 'stable' | 'preview' | 'next';\n}\n\nexport interface TheiaUpdater extends RpcServer<TheiaUpdaterClient> {\n    checkForUpdates(): void;\n    downloadUpdate(): void;\n    onRestartToUpdateRequested(): void;\n    disconnectClient(client: TheiaUpdaterClient): void;\n    cancel(): void;\n    setUpdaterSettings(settings: UpdaterSettings): void;\n}\n\nexport const TheiaUpdaterClient = Symbol('TheiaUpdaterClient');\n\nexport interface UpdaterError {\n    message: string;\n    errorLogPath?: string;\n}\n\nexport interface UpdateInfo {\n    version: string;\n}\n\nexport interface UpdateAvailabilityInfo {\n    available: boolean;\n    updateInfo?: UpdateInfo;\n}\n\nexport interface TheiaUpdaterClient {\n    updateAvailable(available: boolean, updateInfo?: UpdateInfo): void;\n    notifyReadyToInstall(): void;\n    reportError(error: UpdaterError): void;\n    reportCancelled(): void;\n}\n"
  },
  {
    "path": "theia-extensions/updater/src/electron-browser/theia-updater-frontend-module.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2020 TypeFox, EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { CommandContribution, MenuContribution } from '@theia/core/lib/common';\nimport { ElectronMenuUpdater, TheiaUpdaterClientImpl, TheiaUpdaterFrontendContribution } from './updater/theia-updater-frontend-contribution';\nimport { TheiaUpdater, TheiaUpdaterClient, TheiaUpdaterPath } from '../common/updater/theia-updater';\nimport { ContainerModule } from '@theia/core/shared/inversify';\nimport { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-source';\nimport { PreferenceContribution } from '@theia/core/lib/common';\nimport { theiaUpdaterPreferenceSchema } from './updater/theia-updater-preferences';\n\nexport default new ContainerModule((bind, _unbind, isBound, rebind) => {\n    bind(ElectronMenuUpdater).toSelf().inSingletonScope();\n    bind(TheiaUpdaterClientImpl).toSelf().inSingletonScope();\n    bind(TheiaUpdaterClient).toService(TheiaUpdaterClientImpl);\n    bind(TheiaUpdater).toDynamicValue(context => {\n        const client = context.container.get(TheiaUpdaterClientImpl);\n        return ElectronIpcConnectionProvider.createProxy(context.container, TheiaUpdaterPath, client);\n    }).inSingletonScope();\n    bind(TheiaUpdaterFrontendContribution).toSelf().inSingletonScope();\n    bind(MenuContribution).toService(TheiaUpdaterFrontendContribution);\n    bind(CommandContribution).toService(TheiaUpdaterFrontendContribution);\n\n    bind(PreferenceContribution).toConstantValue({ schema: theiaUpdaterPreferenceSchema });\n});\n"
  },
  {
    "path": "theia-extensions/updater/src/electron-browser/updater/theia-updater-frontend-contribution.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2020 TypeFox, EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport {\n    Command,\n    CommandContribution,\n    CommandRegistry,\n    Emitter,\n    MenuContribution,\n    MenuModelRegistry,\n    MenuPath,\n    MessageService,\n    Progress\n} from '@theia/core/lib/common';\nimport { PreferenceScope, PreferenceService } from '@theia/core/lib/common';\nimport { TheiaUpdater, TheiaUpdaterClient, UpdaterError, UpdateInfo, UpdateAvailabilityInfo, UpdaterSettings } from '../../common/updater/theia-updater';\nimport { inject, injectable, postConstruct } from '@theia/core/shared/inversify';\nimport { CommonMenus, OpenerService } from '@theia/core/lib/browser';\nimport { ElectronMainMenuFactory } from '@theia/core/lib/electron-browser/menu/electron-main-menu-factory';\nimport URI from '@theia/core/lib/common/uri';\nimport { URI as VSCodeURI } from 'vscode-uri';\n\nexport namespace TheiaUpdaterCommands {\n\n    const category = 'Theia Electron Updater';\n\n    export const CHECK_FOR_UPDATES: Command = {\n        id: 'electron-theia:check-for-updates',\n        label: 'Check for Updates...',\n        category\n    };\n\n    export const RESTART_TO_UPDATE: Command = {\n        id: 'electron-theia:restart-to-update',\n        label: 'Restart to Update',\n        category\n    };\n\n}\n\nexport namespace TheiaUpdaterMenu {\n    export const MENU_PATH: MenuPath = [...CommonMenus.FILE_SETTINGS_SUBMENU, '3_settings_submenu_update'];\n}\n\n@injectable()\nexport class TheiaUpdaterClientImpl implements TheiaUpdaterClient {\n\n    protected readonly onReadyToInstallEmitter = new Emitter<void>();\n    readonly onReadyToInstall = this.onReadyToInstallEmitter.event;\n\n    protected readonly onUpdateAvailableEmitter = new Emitter<UpdateAvailabilityInfo>();\n    readonly onUpdateAvailable = this.onUpdateAvailableEmitter.event;\n\n    protected readonly onErrorEmitter = new Emitter<UpdaterError>();\n    readonly onError = this.onErrorEmitter.event;\n\n    protected readonly onCancelEmitter = new Emitter<void>();\n    readonly onCancel = this.onCancelEmitter.event;\n\n    notifyReadyToInstall(): void {\n        this.onReadyToInstallEmitter.fire();\n    }\n\n    updateAvailable(available: boolean, updateInfo?: UpdateInfo): void {\n        this.onUpdateAvailableEmitter.fire({ available, updateInfo });\n    }\n\n    reportError(error: UpdaterError): void {\n        this.onErrorEmitter.fire(error);\n    }\n\n    reportCancelled(): void {\n        this.onCancelEmitter.fire();\n    }\n\n}\n\n// Dynamic menus aren't yet supported by electron: https://github.com/eclipse-theia/theia/issues/446\n@injectable()\nexport class ElectronMenuUpdater {\n\n    @inject(ElectronMainMenuFactory)\n    protected readonly factory: ElectronMainMenuFactory;\n\n    public update(): void {\n        this.setMenu();\n    }\n\n    private setMenu(): void {\n        window.electronTheiaCore.setMenu(this.factory.createElectronMenuBar());\n    }\n\n}\n\n@injectable()\nexport class TheiaUpdaterFrontendContribution implements CommandContribution, MenuContribution {\n\n    @inject(MessageService)\n    protected readonly messageService: MessageService;\n\n    @inject(ElectronMenuUpdater)\n    protected readonly menuUpdater: ElectronMenuUpdater;\n\n    @inject(TheiaUpdater)\n    protected readonly updater: TheiaUpdater;\n\n    @inject(TheiaUpdaterClientImpl)\n    protected readonly updaterClient: TheiaUpdaterClientImpl;\n\n    @inject(PreferenceService)\n    private readonly preferenceService: PreferenceService;\n\n    @inject(OpenerService)\n    protected readonly openerService: OpenerService;\n\n    protected readyToUpdate = false;\n\n    private progress: Progress | undefined;\n    private intervalId: NodeJS.Timeout | undefined;\n    private currentUpdateInfo: UpdateInfo | undefined;\n\n    @postConstruct()\n    protected init(): void {\n        this.updaterClient.onUpdateAvailable(({ available, updateInfo }) => {\n            if (available) {\n                this.currentUpdateInfo = updateInfo;\n                this.handleDownloadUpdate(updateInfo);\n            } else {\n                this.handleNoUpdate();\n            }\n        });\n\n        this.updaterClient.onReadyToInstall(async () => {\n            this.readyToUpdate = true;\n            this.menuUpdater.update();\n            this.handleUpdatesAvailable();\n        });\n\n        this.updaterClient.onError(error => this.handleError(error));\n        this.updaterClient.onCancel(() => this.stopProgress());\n\n        this.preferenceService.ready.then(() => {\n            this.syncUpdaterSettings();\n        });\n        this.preferenceService.onPreferenceChanged(e => {\n            if (e.preferenceName === 'updates.checkForUpdates' ||\n                e.preferenceName === 'updates.checkInterval' ||\n                e.preferenceName === 'updates.channel') {\n                this.syncUpdaterSettings();\n            }\n        });\n    }\n\n    protected syncUpdaterSettings(): void {\n        const settings: UpdaterSettings = {\n            checkForUpdates: this.preferenceService.get<boolean>('updates.checkForUpdates', true),\n            checkInterval: this.preferenceService.get<number>('updates.checkInterval', 60),\n            channel: this.preferenceService.get<'stable' | 'preview' | 'next'>('updates.channel', 'stable')\n        };\n        this.updater.setUpdaterSettings(settings);\n    }\n\n    registerCommands(registry: CommandRegistry): void {\n        registry.registerCommand(TheiaUpdaterCommands.CHECK_FOR_UPDATES, {\n            execute: async () => {\n                this.updater.checkForUpdates();\n            },\n            isEnabled: () => !this.readyToUpdate,\n            isVisible: () => !this.readyToUpdate\n        });\n        registry.registerCommand(TheiaUpdaterCommands.RESTART_TO_UPDATE, {\n            execute: () => this.updater.onRestartToUpdateRequested(),\n            isEnabled: () => this.readyToUpdate,\n            isVisible: () => this.readyToUpdate\n        });\n    }\n\n    registerMenus(registry: MenuModelRegistry): void {\n        registry.registerMenuAction(TheiaUpdaterMenu.MENU_PATH, {\n            commandId: TheiaUpdaterCommands.CHECK_FOR_UPDATES.id\n        });\n        registry.registerMenuAction(TheiaUpdaterMenu.MENU_PATH, {\n            commandId: TheiaUpdaterCommands.RESTART_TO_UPDATE.id\n        });\n    }\n\n    protected async handleDownloadUpdate(updateInfo?: UpdateInfo): Promise<void> {\n        const message = updateInfo\n            ? `Update to version ${updateInfo.version} found, do you want to update?`\n            : 'Updates found, do you want to update?';\n        const actions = ['Not now', 'Yes'];\n        const checkForUpdates = this.preferenceService.get<boolean>('updates.checkForUpdates', true);\n        if (checkForUpdates) {\n            actions.push('Never');\n        }\n        const answer = await this.messageService.info(message, ...actions);\n        if (answer === 'Never') {\n            this.preferenceService.set('updates.checkForUpdates', false, PreferenceScope.User);\n            return;\n        }\n        if (answer === 'Yes') {\n            this.stopProgress();\n            this.progress = await this.messageService.showProgress({\n                text: 'Theia IDE Update',\n                options: { cancelable: true }\n            }, () => this.updater.cancel());\n            let dots = 0;\n            this.intervalId = setInterval(() => {\n                if (this.progress !== undefined) {\n                    dots = (dots + 1) % 4;\n                    this.progress.report({ message: 'Downloading' + '.'.repeat(dots) });\n                }\n            }, 1000);\n            this.updater.downloadUpdate();\n        }\n    }\n\n    protected async handleNoUpdate(): Promise<void> {\n        this.messageService.info('Already using the latest version');\n    }\n\n    protected async handleUpdatesAvailable(): Promise<void> {\n        if (this.progress !== undefined) {\n            this.progress.report({ work: { done: 1, total: 1 } });\n            this.stopProgress();\n        }\n        const message = this.currentUpdateInfo\n            ? `An update to version ${this.currentUpdateInfo.version} has been downloaded and will be automatically installed on exit. Do you want to restart now?`\n            : 'An update has been downloaded and will be automatically installed on exit. Do you want to restart now?';\n        const answer = await this.messageService.info(message, 'No', 'Yes');\n        if (answer === 'Yes') {\n            this.updater.onRestartToUpdateRequested();\n        }\n    }\n\n    protected async handleError(error: UpdaterError): Promise<void> {\n        this.stopProgress();\n        if (error.errorLogPath) {\n            const viewLogAction = 'View Error Log';\n            const answer = await this.messageService.error(error.message, viewLogAction);\n            if (answer === viewLogAction) {\n                const uri = new URI(VSCodeURI.file(error.errorLogPath));\n                const opener = await this.openerService.getOpener(uri);\n                opener.open(uri);\n            }\n        } else {\n            this.messageService.error(error.message);\n        }\n    }\n\n    private stopProgress(): void {\n        if (this.intervalId !== undefined) {\n            clearInterval(this.intervalId);\n            this.intervalId = undefined;\n        }\n        if (this.progress !== undefined) {\n            this.progress.cancel();\n            this.progress = undefined;\n        }\n    }\n}\n"
  },
  {
    "path": "theia-extensions/updater/src/electron-browser/updater/theia-updater-preferences.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2020 EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { PreferenceSchema, PreferenceScope } from '@theia/core';\nimport { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';\n\nconst DEFAULT_UPDATE_CHANNELS = ['stable', 'preview'];\n\nfunction getAvailableUpdateChannels(): string[] {\n    try {\n        const config = FrontendApplicationConfigProvider.get() as Record<string, unknown>;\n        return (config['availableUpdateChannels'] as string[]) ?? DEFAULT_UPDATE_CHANNELS;\n    } catch {\n        return DEFAULT_UPDATE_CHANNELS;\n    }\n}\n\nexport const theiaUpdaterPreferenceSchema: PreferenceSchema = {\n    'properties': {\n        'updates.checkForUpdates': {\n            type: 'boolean',\n            description: 'Automatically check for updates.',\n            default: true,\n            scope: PreferenceScope.User\n        },\n        'updates.checkInterval': {\n            type: 'number',\n            description: 'Interval in minutes between automatic update checks.',\n            default: 60,\n            scope: PreferenceScope.User\n        },\n        'updates.channel': {\n            type: 'string',\n            enum: getAvailableUpdateChannels(),\n            description: 'Channel to use for updates.',\n            default: getAvailableUpdateChannels()[0] ?? '',\n            scope: PreferenceScope.User\n        },\n    }\n};\n"
  },
  {
    "path": "theia-extensions/updater/src/electron-main/update/theia-updater-impl.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2020 TypeFox, EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport * as fs from 'fs-extra';\nimport * as http from 'http';\nimport * as os from 'os';\nimport * as path from 'path';\nimport { ElectronMainApplication, ElectronMainApplicationContribution } from '@theia/core/lib/electron-main/electron-main-application';\nimport { TheiaUpdater, TheiaUpdaterClient, UpdaterSettings } from '../../common/updater/theia-updater';\nimport { injectable } from '@theia/core/shared/inversify';\nimport { isOSX, isWindows } from '@theia/core';\nimport { CancellationToken } from 'builder-util-runtime';\n\nconst STABLE_CHANNEL_WINDOWS = 'https://download.eclipse.org/theia/ide/version/windows';\nconst STABLE_CHANNEL_MACOS = 'https://download.eclipse.org/theia/ide/latest/macos';\nconst STABLE_CHANNEL_MACOS_ARM = 'https://download.eclipse.org/theia/ide/latest/macos-arm';\nconst STABLE_CHANNEL_LINUX = 'https://download.eclipse.org/theia/ide/latest/linux';\n\nconst PREVIEW_CHANNEL_WINDOWS = 'https://download.eclipse.org/theia/ide-preview/version/windows';\nconst PREVIEW_CHANNEL_MACOS = 'https://download.eclipse.org/theia/ide-preview/latest/macos';\nconst PREVIEW_CHANNEL_MACOS_ARM = 'https://download.eclipse.org/theia/ide-preview/latest/macos-arm';\nconst PREVIEW_CHANNEL_LINUX = 'https://download.eclipse.org/theia/ide-preview/latest/linux';\n\n// Next updates are currently only available for Linux.\n// The feed is served from GitHub Release assets (rolling \"next\" tag).\nconst NEXT_CHANNEL_LINUX = 'https://github.com/eclipse-theia/theia-ide/releases/download/next';\n\nconst { autoUpdater } = require('electron-updater');\n\nautoUpdater.logger = require('electron-log');\nautoUpdater.logger.transports.file.level = 'info';\n\n@injectable()\nexport class TheiaUpdaterImpl implements TheiaUpdater, ElectronMainApplicationContribution {\n\n    protected clients: Array<TheiaUpdaterClient> = [];\n    protected settings: UpdaterSettings = {\n        checkForUpdates: true,\n        checkInterval: 60,\n        channel: 'stable'\n    };\n\n    private initialCheck: boolean = true;\n    private reportOnFirstRegistration: boolean = false;\n    private cancellationToken: CancellationToken = new CancellationToken();\n    private updateCheckTimer: NodeJS.Timeout | undefined;\n\n    constructor() {\n        autoUpdater.autoDownload = false;\n        autoUpdater.on('update-available', (info: { version: string }) => {\n            if (this.initialCheck) {\n                this.initialCheck = false;\n                if (this.clients.length === 0) {\n                    this.reportOnFirstRegistration = true;\n                }\n            }\n            const updateInfo = { version: info.version };\n            this.clients.forEach(c => c.updateAvailable(true, updateInfo));\n        });\n        autoUpdater.on('update-not-available', () => {\n            if (this.initialCheck) {\n                this.initialCheck = false;\n                return;\n            }\n            this.clients.forEach(c => c.updateAvailable(false));\n        });\n\n        autoUpdater.on('update-downloaded', () => {\n            this.clients.forEach(c => c.notifyReadyToInstall());\n        });\n\n        autoUpdater.on('error', (err: unknown) => {\n            if (err instanceof Error && err.message.includes('cancelled')) {\n                return;\n            }\n            const errorLogPath = autoUpdater.logger.transports.file.getFile().path;\n            this.clients.forEach(c => c.reportError({ message: 'An error has occurred while attempting to update.', errorLogPath }));\n        });\n    }\n\n    checkForUpdates(): void {\n        const feedURL = this.getFeedURL(this.settings.channel);\n        autoUpdater.setFeedURL(feedURL);\n        autoUpdater.checkForUpdates();\n    }\n\n    setUpdaterSettings(settings: UpdaterSettings): void {\n        const settingsChanged = this.settings.checkForUpdates !== settings.checkForUpdates ||\n            this.settings.checkInterval !== settings.checkInterval ||\n            this.settings.channel !== settings.channel;\n        this.settings = settings;\n        if (settingsChanged) {\n            this.scheduleUpdateChecks();\n        }\n    }\n\n    onRestartToUpdateRequested(): void {\n        autoUpdater.quitAndInstall();\n    }\n\n    cancel(): void {\n        autoUpdater.logger.info('Update cancelled by user');\n        this.cancellationToken.cancel();\n        this.clients.forEach(c => c.reportCancelled());\n    }\n\n    downloadUpdate(): void {\n        autoUpdater.logger.info('Downloading update');\n        this.cancellationToken = new CancellationToken();\n        autoUpdater.downloadUpdate(this.cancellationToken);\n\n        // record download stat, ignore errors\n        fs.mkdtemp(path.join(os.tmpdir(), 'updater-'))\n            .then(tmpDir => {\n                const file = fs.createWriteStream(path.join(tmpDir, 'update'));\n                http.get('https://www.eclipse.org/downloads/download.php?file=/theia/update&r=1', response => {\n                    response.pipe(file);\n                    file.on('finish', () => {\n                        file.close();\n                    });\n                });\n            });\n    }\n\n    onStart(application: ElectronMainApplication): void {\n    }\n\n    onStop(application: ElectronMainApplication): void {\n        this.stopUpdateCheckTimer();\n    }\n\n    private scheduleUpdateChecks(): void {\n        this.stopUpdateCheckTimer();\n\n        if (!this.settings.checkForUpdates) {\n            return;\n        }\n\n        this.checkForUpdates();\n\n        const intervalMs = Math.max(this.settings.checkInterval, 1) * 60 * 1000;\n\n        this.updateCheckTimer = setInterval(() => {\n            if (this.settings.checkForUpdates) {\n                this.checkForUpdates();\n            }\n        }, intervalMs);\n    }\n\n    private stopUpdateCheckTimer(): void {\n        if (this.updateCheckTimer) {\n            clearInterval(this.updateCheckTimer);\n            this.updateCheckTimer = undefined;\n        }\n    }\n\n    setClient(client: TheiaUpdaterClient | undefined): void {\n        if (client) {\n            this.clients.push(client);\n            if (this.reportOnFirstRegistration) {\n                this.reportOnFirstRegistration = false;\n                this.clients.forEach(c => c.updateAvailable(true));\n            }\n        }\n    }\n\n    protected getFeedURL(channel: string): string {\n        if (isWindows) {\n            const curVersion = autoUpdater.currentVersion.toString();\n            // Next not yet available on Windows, fall back to stable\n            return (channel === 'preview') ? PREVIEW_CHANNEL_WINDOWS.replace('version', curVersion) : STABLE_CHANNEL_WINDOWS.replace('version', curVersion);\n        } else if (isOSX) {\n            // Next not yet available on macOS, fall back to stable\n            if (process.arch === 'arm64') {\n                return (channel === 'preview') ? PREVIEW_CHANNEL_MACOS_ARM : STABLE_CHANNEL_MACOS_ARM;\n            } else {\n                return (channel === 'preview') ? PREVIEW_CHANNEL_MACOS : STABLE_CHANNEL_MACOS;\n            }\n        } else {\n            if (channel === 'next') {\n                return NEXT_CHANNEL_LINUX;\n            }\n            return (channel === 'preview') ? PREVIEW_CHANNEL_LINUX : STABLE_CHANNEL_LINUX;\n        }\n    }\n\n    disconnectClient(client: TheiaUpdaterClient): void {\n        const index = this.clients.indexOf(client);\n        if (index !== -1) {\n            this.clients.splice(index, 1);\n        }\n    }\n\n    dispose(): void {\n        this.stopUpdateCheckTimer();\n        this.clients.forEach(this.disconnectClient.bind(this));\n    }\n\n}\n"
  },
  {
    "path": "theia-extensions/updater/src/electron-main/update/theia-updater-main-module.ts",
    "content": "/********************************************************************************\n * Copyright (C) 2020 TypeFox, EclipseSource and others.\n *\n * This program and the accompanying materials are made available under the\n * terms of the MIT License, which is available in the project root.\n *\n * SPDX-License-Identifier: MIT\n ********************************************************************************/\n\nimport { TheiaUpdater, TheiaUpdaterClient, TheiaUpdaterPath } from '../../common/updater/theia-updater';\nimport { ContainerModule } from '@theia/core/shared/inversify';\nimport { ElectronConnectionHandler } from '@theia/core/lib/electron-main/messaging/electron-connection-handler';\nimport { ElectronMainApplicationContribution } from '@theia/core/lib/electron-main/electron-main-application';\nimport { JsonRpcConnectionHandler } from '@theia/core/lib/common/messaging/proxy-factory';\nimport { TheiaUpdaterImpl } from './theia-updater-impl';\n\nexport default new ContainerModule(bind => {\n    bind(TheiaUpdaterImpl).toSelf().inSingletonScope();\n    bind(TheiaUpdater).toService(TheiaUpdaterImpl);\n    bind(ElectronMainApplicationContribution).toService(TheiaUpdater);\n    bind(ElectronConnectionHandler).toDynamicValue(context =>\n        new JsonRpcConnectionHandler<TheiaUpdaterClient>(TheiaUpdaterPath, client => {\n            const server = context.container.get<TheiaUpdater>(TheiaUpdater);\n            server.setClient(client);\n            client.onDidCloseConnection(() => server.disconnectClient(client));\n            return server;\n        })\n    ).inSingletonScope();\n});\n"
  },
  {
    "path": "theia-extensions/updater/tsconfig.json",
    "content": "{\n    \"extends\": \"../../configs/base.tsconfig\",\n    \"compilerOptions\": {\n        \"rootDir\": \"src\",\n        \"outDir\": \"lib\",\n        \"baseUrl\": \".\",\n        \"esModuleInterop\": true\n    },\n    \"include\": [\n        \"src\",\n    ]\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"extends\": \"./configs/base.tsconfig.json\",\n  \"include\": [],\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"allowJs\": true\n  },\n  \"references\": [\n    {\n      \"path\": \"theia-extensions/launcher\"\n    },\n    {\n      \"path\": \"theia-extensions/product\"\n    },\n    {\n      \"path\": \"theia-extensions/updater\"\n    },\n    {\n      \"path\": \"applications/electron\"\n    },\n    {\n      \"path\": \"applications/browser\"\n    },\n  ]\n}\n"
  }
]