Compare commits

...

21 commits

Author SHA1 Message Date
Asher
472bf8a5fa
Remove homebrew step
It has been broken for a long time (No available formula with the name
"code-server") but it looks like they have their own bot publishing
updates anyway.
2025-12-02 13:38:27 -09:00
dependabot[bot]
eccb1eb537
chore: bump actions/setup-node from 4 to 6 (#7550) 2025-12-02 13:04:45 -09:00
Asher
f128a7ac11
Update actionlint to 1.7.9 2025-12-01 12:55:13 -09:00
Asher
80996d2e08
Add VS Code web to the bug template 2025-12-01 12:49:28 -09:00
dependabot[bot]
9819b91c74
chore: bump actions/checkout from 5 to 6 (#7585) 2025-12-01 12:47:25 -09:00
Olivier Benz
2ed1098c1e
Update Code to 1.106.3 (#7583) 2025-12-01 11:52:45 -09:00
Asher
85042e2910
Note settings change from VS Code web and Codespaces 2025-11-25 14:39:16 -09:00
Asher
904942a194
Increase build timeout 2025-11-19 14:02:53 -09:00
Olivier Benz
9a24e467b2
Update Code to 1.106.2 (#7571) 2025-11-19 12:54:38 -09:00
Olivier Benz
24a777491b
Update Code to 1.106.1 (#7570) 2025-11-19 09:09:30 -09:00
Asher
93c1f4f10c
Update macOS runner
Seems macos-13 is being deprecated.
2025-11-18 13:25:39 -09:00
Olivier Benz
339c3926c2
Update Code to 1.106.0 (#7569) 2025-11-18 11:21:41 -09:00
dependabot[bot]
897b5f13bc
chore: bump playwright and @playwright/test in /test (#7534) 2025-10-28 16:14:42 -08:00
SuitDeer
282f74d9f5
Update Node.js version in Android docs from 18 to 22 (#7542) 2025-10-28 16:14:17 -08:00
dependabot[bot]
7a2a5eb055
chore: bump eslint from 9.32.0 to 9.36.0 (#7513) 2025-10-28 16:13:34 -08:00
dependabot[bot]
af397f71e2
chore: bump globals from 16.1.0 to 16.4.0 (#7511) 2025-10-28 16:13:15 -08:00
dependabot[bot]
9d89b17fd7
chore: bump express and @types/express (#7510) 2025-10-28 16:12:45 -08:00
dependabot[bot]
35e7b09a85
chore: bump actions/checkout from 4 to 5 (#7508) 2025-10-28 16:12:00 -08:00
dependabot[bot]
7beb05d04f
chore: bump aquasecurity/trivy-action from 0.32.0 to 0.33.1 (#7507) 2025-10-28 16:11:42 -08:00
dependabot[bot]
add51d5c5b
chore: bump actions/download-artifact from 4 to 5 (#7506) 2025-10-28 16:11:23 -08:00
Andrew Baldwin
db8a41bce1
Add idle timeout (#7539) 2025-10-28 16:10:56 -08:00
29 changed files with 373 additions and 380 deletions

View file

@ -86,6 +86,18 @@ body:
validations: validations:
required: true required: true
- type: dropdown
attributes:
label: Does this bug reproduce in VS Code web?
description: If the bug reproduces in VS Code web, submit the issue upstream instead (https://github.com/microsoft/vscode). You can run VS Code web with `code serve-web`.
options:
- Yes, this is also broken in VS Code web
- No, this works as expected in VS Code web
- This cannot be tested in VS Code web
- I did not test VS Code web
validations:
required: true
- type: dropdown - type: dropdown
attributes: attributes:
label: Does this bug reproduce in GitHub Codespaces? label: Does this bug reproduce in GitHub Codespaces?

View file

@ -32,7 +32,7 @@ jobs:
helm: ${{ steps.filter.outputs.helm }} helm: ${{ steps.filter.outputs.helm }}
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Check changed files - name: Check changed files
uses: dorny/paths-filter@v3 uses: dorny/paths-filter@v3
id: filter id: filter
@ -64,8 +64,8 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
timeout-minutes: 5 timeout-minutes: 5
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-node@v4 - uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
cache: npm cache: npm
@ -82,8 +82,8 @@ jobs:
needs: changes needs: changes
if: needs.changes.outputs.docs == 'true' if: needs.changes.outputs.docs == 'true'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-node@v4 - uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
cache: npm cache: npm
@ -100,7 +100,7 @@ jobs:
needs: changes needs: changes
if: needs.changes.outputs.helm == 'true' if: needs.changes.outputs.helm == 'true'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: azure/setup-helm@v4 - uses: azure/setup-helm@v4
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
@ -114,8 +114,8 @@ jobs:
needs: changes needs: changes
if: needs.changes.outputs.code == 'true' if: needs.changes.outputs.code == 'true'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-node@v4 - uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
cache: npm cache: npm
@ -132,11 +132,11 @@ jobs:
if: needs.changes.outputs.ci == 'true' if: needs.changes.outputs.ci == 'true'
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Check workflow files - name: Check workflow files
run: | run: |
bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) 1.7.1 bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) 1.7.9
./actionlint -color -shellcheck= -ignore "set-output" ./actionlint -color -shellcheck= -ignore "softprops/action-gh-release"
shell: bash shell: bash
test-unit: test-unit:
@ -146,8 +146,8 @@ jobs:
needs: changes needs: changes
if: needs.changes.outputs.code == 'true' if: needs.changes.outputs.code == 'true'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-node@v4 - uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
cache: npm cache: npm
@ -164,12 +164,12 @@ jobs:
build: build:
name: Build code-server name: Build code-server
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
timeout-minutes: 60 timeout-minutes: 70
env: env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
DISABLE_V8_COMPILE_CACHE: 1 DISABLE_V8_COMPILE_CACHE: 1
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
with: with:
submodules: true submodules: true
- run: sudo apt update && sudo apt install -y libkrb5-dev - run: sudo apt update && sudo apt install -y libkrb5-dev
@ -178,7 +178,7 @@ jobs:
packages: quilt packages: quilt
version: 1.0 version: 1.0
- run: quilt push -a - run: quilt push -a
- uses: actions/setup-node@v4 - uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
cache: npm cache: npm
@ -231,9 +231,9 @@ jobs:
needs: [changes, build] needs: [changes, build]
if: needs.changes.outputs.code == 'true' || needs.changes.outputs.deps == 'true' if: needs.changes.outputs.code == 'true' || needs.changes.outputs.deps == 'true'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- run: sudo apt update && sudo apt install -y libkrb5-dev - run: sudo apt update && sudo apt install -y libkrb5-dev
- uses: actions/setup-node@v4 - uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
cache: npm cache: npm
@ -241,7 +241,7 @@ jobs:
package-lock.json package-lock.json
test/package-lock.json test/package-lock.json
- run: SKIP_SUBMODULE_DEPS=1 npm ci - run: SKIP_SUBMODULE_DEPS=1 npm ci
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v5
with: with:
name: npm-package name: npm-package
- run: tar -xzf package.tar.gz - run: tar -xzf package.tar.gz
@ -265,9 +265,9 @@ jobs:
needs: [changes, build] needs: [changes, build]
if: needs.changes.outputs.code == 'true' || needs.changes.outputs.deps == 'true' if: needs.changes.outputs.code == 'true' || needs.changes.outputs.deps == 'true'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- run: sudo apt update && sudo apt install -y libkrb5-dev - run: sudo apt update && sudo apt install -y libkrb5-dev
- uses: actions/setup-node@v4 - uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
cache: npm cache: npm
@ -275,7 +275,7 @@ jobs:
package-lock.json package-lock.json
test/package-lock.json test/package-lock.json
- run: SKIP_SUBMODULE_DEPS=1 npm ci - run: SKIP_SUBMODULE_DEPS=1 npm ci
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v5
with: with:
name: npm-package name: npm-package
- run: tar -xzf package.tar.gz - run: tar -xzf package.tar.gz

View file

@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Install code-server - name: Install code-server
run: ./install.sh run: ./install.sh
@ -44,7 +44,7 @@ jobs:
container: "alpine:3.17" container: "alpine:3.17"
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Install curl - name: Install curl
run: apk add curl run: apk add curl
@ -67,7 +67,7 @@ jobs:
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Install code-server - name: Install code-server
run: ./install.sh run: ./install.sh

View file

@ -25,10 +25,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code-server - name: Checkout code-server
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
@ -53,38 +53,6 @@ jobs:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_ENVIRONMENT: "production" NPM_ENVIRONMENT: "production"
homebrew:
needs: npm
runs-on: ubuntu-latest
steps:
# Ensure things are up to date
# Suggested by homebrew maintainers
# https://github.com/Homebrew/discussions/discussions/1532#discussioncomment-782633
- name: Set up Homebrew
id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master
- name: Checkout code-server
uses: actions/checkout@v4
- name: Configure git
run: |
git config --global user.name cdrci
git config --global user.email opensource@coder.com
# Strip out the v (v4.9.1 -> 4.9.1).
- name: Get and set VERSION
run: |
TAG="${{ github.event.inputs.version || github.ref_name }}"
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
- name: Bump code-server homebrew version
env:
VERSION: ${{ env.VERSION }}
HOMEBREW_GITHUB_API_TOKEN: ${{secrets.HOMEBREW_GITHUB_API_TOKEN}}
run: ./ci/steps/brew-bump.sh
aur: aur:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 10 timeout-minutes: 10
@ -94,13 +62,13 @@ jobs:
steps: steps:
# We need to checkout code-server so we can get the version # We need to checkout code-server so we can get the version
- name: Checkout code-server - name: Checkout code-server
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
path: "./code-server" path: "./code-server"
- name: Checkout code-server-aur repo - name: Checkout code-server-aur repo
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
repository: "cdrci/code-server-aur" repository: "cdrci/code-server-aur"
token: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }} token: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}
@ -148,7 +116,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code-server - name: Checkout code-server
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3

View file

@ -60,10 +60,10 @@ jobs:
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
cache: npm cache: npm
@ -94,7 +94,7 @@ jobs:
echo "$HOME/.local/bin" >> $GITHUB_PATH echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Download npm package - name: Download npm package
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
name: npm-release-package name: npm-release-package
@ -126,7 +126,7 @@ jobs:
package-macos-amd64: package-macos-amd64:
name: x86-64 macOS build name: x86-64 macOS build
runs-on: macos-13 runs-on: macos-15-intel
timeout-minutes: 15 timeout-minutes: 15
needs: npm-version needs: npm-version
env: env:
@ -134,10 +134,10 @@ jobs:
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
cache: npm cache: npm
@ -160,7 +160,7 @@ jobs:
- run: brew install python-setuptools - run: brew install python-setuptools
- name: Download npm package - name: Download npm package
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
name: npm-release-package name: npm-release-package
@ -195,10 +195,10 @@ jobs:
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
cache: npm cache: npm
@ -221,7 +221,7 @@ jobs:
- run: brew install python-setuptools - run: brew install python-setuptools
- name: Download npm package - name: Download npm package
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
name: npm-release-package name: npm-release-package
@ -253,7 +253,7 @@ jobs:
needs: npm-version needs: npm-version
steps: steps:
- name: Download npm package - name: Download npm package
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
name: npm-release-package name: npm-release-package

View file

@ -41,7 +41,7 @@ jobs:
container: "alpine:3.17" container: "alpine:3.17"
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Install test utilities - name: Install test utilities
run: apk add bats checkbashisms run: apk add bats checkbashisms
@ -58,7 +58,7 @@ jobs:
timeout-minutes: 5 timeout-minutes: 5
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Install lint utilities - name: Install lint utilities
run: sudo apt install shellcheck run: sudo apt install shellcheck

View file

@ -25,12 +25,12 @@ jobs:
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v6
with: with:
node-version-file: .node-version node-version-file: .node-version
@ -46,12 +46,12 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Run Trivy vulnerability scanner in repo mode - name: Run Trivy vulnerability scanner in repo mode
uses: aquasecurity/trivy-action@dc5a429b52fcf669ce959baa2c2dd26090d2a6c4 uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8
with: with:
scan-type: "fs" scan-type: "fs"
scan-ref: "." scan-ref: "."
@ -76,7 +76,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL

View file

@ -48,10 +48,10 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Run Trivy vulnerability scanner in image mode - name: Run Trivy vulnerability scanner in image mode
uses: aquasecurity/trivy-action@dc5a429b52fcf669ce959baa2c2dd26090d2a6c4 uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8
with: with:
image-ref: "docker.io/codercom/code-server:latest" image-ref: "docker.io/codercom/code-server:latest"
ignore-unfixed: true ignore-unfixed: true

View file

@ -1 +1 @@
22.19.0 22.20.0

View file

@ -31,6 +31,7 @@
- [What's the difference between code-server and Theia?](#whats-the-difference-between-code-server-and-theia) - [What's the difference between code-server and Theia?](#whats-the-difference-between-code-server-and-theia)
- [What's the difference between code-server and OpenVSCode-Server?](#whats-the-difference-between-code-server-and-openvscode-server) - [What's the difference between code-server and OpenVSCode-Server?](#whats-the-difference-between-code-server-and-openvscode-server)
- [What's the difference between code-server and GitHub Codespaces?](#whats-the-difference-between-code-server-and-github-codespaces) - [What's the difference between code-server and GitHub Codespaces?](#whats-the-difference-between-code-server-and-github-codespaces)
- [What's the difference between code-server and VS Code web?](#whats-the-difference-between-code-server-and-vs-code-web)
- [Does code-server have any security login validation?](#does-code-server-have-any-security-login-validation) - [Does code-server have any security login validation?](#does-code-server-have-any-security-login-validation)
- [Are there community projects involving code-server?](#are-there-community-projects-involving-code-server) - [Are there community projects involving code-server?](#are-there-community-projects-involving-code-server)
- [How do I change the port?](#how-do-i-change-the-port) - [How do I change the port?](#how-do-i-change-the-port)
@ -322,12 +323,8 @@ As long as there is an active browser connection, code-server touches
`~/.local/share/code-server/heartbeat` once a minute. `~/.local/share/code-server/heartbeat` once a minute.
If you want to shutdown code-server if there hasn't been an active connection If you want to shutdown code-server if there hasn't been an active connection
after a predetermined amount of time, you can do so by checking continuously for after a predetermined amount of time, you can use the --idle-timeout-seconds flag
the last modified time on the heartbeat file. If it is older than X minutes (or or set an `CODE_SERVER_IDLE_TIMEOUT_SECONDS` environment variable.
whatever amount of time you'd like), you can kill code-server.
Eventually, [#1636](https://github.com/coder/code-server/issues/1636) will make
this process better.
## How do I change the password? ## How do I change the password?
@ -443,6 +440,8 @@ Specific changes include:
- The ability to use your own marketplace and collect your own telemetry - The ability to use your own marketplace and collect your own telemetry
- Built-in proxy for accessing ports on the remote machine integrated into - Built-in proxy for accessing ports on the remote machine integrated into
VS Code's ports panel VS Code's ports panel
- Settings are stored on disk like desktop VS Code, instead of in browser
storage (note that state is still stored in browser storage).
- Wrapper process that spawns VS Code on-demand and has a separate CLI - Wrapper process that spawns VS Code on-demand and has a separate CLI
- Notification when updates are available - Notification when updates are available
- [Some other things](https://github.com/coder/code-server/tree/main/patches) - [Some other things](https://github.com/coder/code-server/tree/main/patches)
@ -451,6 +450,12 @@ Some of these changes appear very unlikely to ever be adopted by Microsoft.
Some may make their way upstream, further closing the gap, but at the moment it Some may make their way upstream, further closing the gap, but at the moment it
looks like there will always be some subtle differences. looks like there will always be some subtle differences.
## What's the difference between code-server and VS Code web?
VS Code web (which can be ran using `code serve-web`) has the same differences
as the Codespaces section above. VS Code web can be a better choice if you need
access to the official Microsoft marketplace.
## Does code-server have any security login validation? ## Does code-server have any security login validation?
code-server supports setting a single password and limits logins to two per code-server supports setting a single password and limits logins to two per

View file

@ -14,8 +14,8 @@ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
7. Install and use Node.js 22: 7. Install and use Node.js 22:
```shell ```shell
nvm install 18 nvm install 22
nvm use 18 nvm use 22
``` ```
8. Install code-server globally on device with: `npm install --global code-server` 8. Install code-server globally on device with: `npm install --global code-server`

@ -1 +1 @@
Subproject commit 7d842fb85a0275a4a8e4d7e040d2625abbf7f084 Subproject commit bf9252a2fb45be6893dd8870c0bf37e2e1766d61

318
package-lock.json generated
View file

@ -128,9 +128,9 @@
} }
}, },
"node_modules/@eslint-community/eslint-utils": { "node_modules/@eslint-community/eslint-utils": {
"version": "4.7.0", "version": "4.9.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
"integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -190,9 +190,9 @@
} }
}, },
"node_modules/@eslint/config-helpers": { "node_modules/@eslint/config-helpers": {
"version": "0.3.0", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz",
"integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
@ -200,9 +200,9 @@
} }
}, },
"node_modules/@eslint/core": { "node_modules/@eslint/core": {
"version": "0.15.1", "version": "0.15.2",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
"integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@ -250,9 +250,9 @@
} }
}, },
"node_modules/@eslint/js": { "node_modules/@eslint/js": {
"version": "9.32.0", "version": "9.36.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz",
"integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -273,13 +273,13 @@
} }
}, },
"node_modules/@eslint/plugin-kit": { "node_modules/@eslint/plugin-kit": {
"version": "0.3.4", "version": "0.3.5",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
"integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@eslint/core": "^0.15.1", "@eslint/core": "^0.15.2",
"levn": "^0.4.1" "levn": "^0.4.1"
}, },
"engines": { "engines": {
@ -610,15 +610,14 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/express": { "node_modules/@types/express": {
"version": "5.0.0", "version": "5.0.3",
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz",
"integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/body-parser": "*", "@types/body-parser": "*",
"@types/express-serve-static-core": "^5.0.0", "@types/express-serve-static-core": "^5.0.0",
"@types/qs": "*",
"@types/serve-static": "*" "@types/serve-static": "*"
} }
}, },
@ -1640,18 +1639,18 @@
} }
}, },
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "2.1.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.1.0.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
"integrity": "sha512-/hPxh61E+ll0Ujp24Ilm64cykicul1ypfwjVttduAiEdtnJFvLePSrIPk+HMImtNv5270wOGCb1Tns2rybMkoQ==", "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"bytes": "^3.1.2", "bytes": "^3.1.2",
"content-type": "^1.0.5", "content-type": "^1.0.5",
"debug": "^4.4.0", "debug": "^4.4.0",
"http-errors": "^2.0.0", "http-errors": "^2.0.0",
"iconv-lite": "^0.5.2", "iconv-lite": "^0.6.3",
"on-finished": "^2.4.1", "on-finished": "^2.4.1",
"qs": "6.14.0", "qs": "^6.14.0",
"raw-body": "^3.0.0", "raw-body": "^3.0.0",
"type-is": "^2.0.0" "type-is": "^2.0.0"
}, },
@ -2150,16 +2149,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"license": "MIT",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/detect-libc": { "node_modules/detect-libc": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
@ -2490,20 +2479,20 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "9.32.0", "version": "9.36.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz",
"integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.21.0", "@eslint/config-array": "^0.21.0",
"@eslint/config-helpers": "^0.3.0", "@eslint/config-helpers": "^0.3.1",
"@eslint/core": "^0.15.0", "@eslint/core": "^0.15.2",
"@eslint/eslintrc": "^3.3.1", "@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.32.0", "@eslint/js": "9.36.0",
"@eslint/plugin-kit": "^0.3.4", "@eslint/plugin-kit": "^0.3.5",
"@humanfs/node": "^0.16.6", "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2", "@humanwhocodes/retry": "^0.4.2",
@ -2903,55 +2892,45 @@
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
}, },
"node_modules/express": { "node_modules/express": {
"version": "5.0.1", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/express/-/express-5.0.1.tgz", "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
"integrity": "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==", "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"accepts": "^2.0.0", "accepts": "^2.0.0",
"body-parser": "^2.0.1", "body-parser": "^2.2.0",
"content-disposition": "^1.0.0", "content-disposition": "^1.0.0",
"content-type": "~1.0.4", "content-type": "^1.0.5",
"cookie": "0.7.1", "cookie": "^0.7.1",
"cookie-signature": "^1.2.1", "cookie-signature": "^1.2.1",
"debug": "4.3.6", "debug": "^4.4.0",
"depd": "2.0.0", "encodeurl": "^2.0.0",
"encodeurl": "~2.0.0", "escape-html": "^1.0.3",
"escape-html": "~1.0.3", "etag": "^1.8.1",
"etag": "~1.8.1", "finalhandler": "^2.1.0",
"finalhandler": "^2.0.0", "fresh": "^2.0.0",
"fresh": "2.0.0", "http-errors": "^2.0.0",
"http-errors": "2.0.0",
"merge-descriptors": "^2.0.0", "merge-descriptors": "^2.0.0",
"methods": "~1.1.2",
"mime-types": "^3.0.0", "mime-types": "^3.0.0",
"on-finished": "2.4.1", "on-finished": "^2.4.1",
"once": "1.4.0", "once": "^1.4.0",
"parseurl": "~1.3.3", "parseurl": "^1.3.3",
"proxy-addr": "~2.0.7", "proxy-addr": "^2.0.7",
"qs": "6.13.0", "qs": "^6.14.0",
"range-parser": "~1.2.1", "range-parser": "^1.2.1",
"router": "^2.0.0", "router": "^2.2.0",
"safe-buffer": "5.2.1",
"send": "^1.1.0", "send": "^1.1.0",
"serve-static": "^2.1.0", "serve-static": "^2.2.0",
"setprototypeof": "1.2.0", "statuses": "^2.0.1",
"statuses": "2.0.1", "type-is": "^2.0.1",
"type-is": "^2.0.0", "vary": "^1.1.2"
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}, },
"engines": { "engines": {
"node": ">= 18" "node": ">= 18"
}
}, },
"node_modules/express/node_modules/cookie": { "funding": {
"version": "0.7.1", "type": "opencollective",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", "url": "https://opencollective.com/express"
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
} }
}, },
"node_modules/express/node_modules/cookie-signature": { "node_modules/express/node_modules/cookie-signature": {
@ -2963,44 +2942,6 @@
"node": ">=6.6.0" "node": ">=6.6.0"
} }
}, },
"node_modules/express/node_modules/debug": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
"license": "MIT",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/express/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"license": "MIT"
},
"node_modules/express/node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/extend": { "node_modules/extend": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@ -3416,9 +3357,9 @@
} }
}, },
"node_modules/globals": { "node_modules/globals": {
"version": "16.1.0", "version": "16.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz",
"integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -3669,12 +3610,12 @@
} }
}, },
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.5.2", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"safer-buffer": ">= 2.1.2 < 3" "safer-buffer": ">= 2.1.2 < 3.0.0"
}, },
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@ -4480,15 +4421,6 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/micromark": { "node_modules/micromark": {
"version": "2.11.4", "version": "2.11.4",
"resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz",
@ -4630,20 +4562,21 @@
} }
}, },
"node_modules/mime-db": { "node_modules/mime-db": {
"version": "1.53.0", "version": "1.54.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
"integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
"license": "MIT",
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/mime-types": { "node_modules/mime-types": {
"version": "3.0.0", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.0.tgz", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
"integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==", "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"mime-db": "^1.53.0" "mime-db": "^1.54.0"
}, },
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
@ -5120,12 +5053,13 @@
"dev": true "dev": true
}, },
"node_modules/path-to-regexp": { "node_modules/path-to-regexp": {
"version": "8.2.0", "version": "8.3.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
"integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
"license": "MIT", "license": "MIT",
"engines": { "funding": {
"node": ">=16" "type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/pem": { "node_modules/pem": {
@ -5334,30 +5268,34 @@
} }
}, },
"node_modules/raw-body": { "node_modules/raw-body": {
"version": "3.0.0", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz",
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"bytes": "3.1.2", "bytes": "3.1.2",
"http-errors": "2.0.0", "http-errors": "2.0.0",
"iconv-lite": "0.6.3", "iconv-lite": "0.7.0",
"unpipe": "1.0.0" "unpipe": "1.0.0"
}, },
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.10"
} }
}, },
"node_modules/raw-body/node_modules/iconv-lite": { "node_modules/raw-body/node_modules/iconv-lite": {
"version": "0.6.3", "version": "0.7.0",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0" "safer-buffer": ">= 2.1.2 < 3.0.0"
}, },
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/readable-stream": { "node_modules/readable-stream": {
@ -5535,11 +5473,13 @@
} }
}, },
"node_modules/router": { "node_modules/router": {
"version": "2.1.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/router/-/router-2.1.0.tgz", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
"integrity": "sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA==", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"debug": "^4.4.0",
"depd": "^2.0.0",
"is-promise": "^4.0.0", "is-promise": "^4.0.0",
"parseurl": "^1.3.3", "parseurl": "^1.3.3",
"path-to-regexp": "^8.0.0" "path-to-regexp": "^8.0.0"
@ -5653,19 +5593,18 @@
} }
}, },
"node_modules/send": { "node_modules/send": {
"version": "1.1.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/send/-/send-1.1.0.tgz", "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
"integrity": "sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==", "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"debug": "^4.3.5", "debug": "^4.3.5",
"destroy": "^1.2.0",
"encodeurl": "^2.0.0", "encodeurl": "^2.0.0",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"etag": "^1.8.1", "etag": "^1.8.1",
"fresh": "^0.5.2", "fresh": "^2.0.0",
"http-errors": "^2.0.0", "http-errors": "^2.0.0",
"mime-types": "^2.1.35", "mime-types": "^3.0.1",
"ms": "^2.1.3", "ms": "^2.1.3",
"on-finished": "^2.4.1", "on-finished": "^2.4.1",
"range-parser": "^1.2.1", "range-parser": "^1.2.1",
@ -5675,46 +5614,16 @@
"node": ">= 18" "node": ">= 18"
} }
}, },
"node_modules/send/node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/send/node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/send/node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/serve-static": { "node_modules/serve-static": {
"version": "2.1.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.1.0.tgz", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
"integrity": "sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==", "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"encodeurl": "^2.0.0", "encodeurl": "^2.0.0",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"parseurl": "^1.3.3", "parseurl": "^1.3.3",
"send": "^1.0.0" "send": "^1.2.0"
}, },
"engines": { "engines": {
"node": ">= 18" "node": ">= 18"
@ -6307,9 +6216,9 @@
} }
}, },
"node_modules/type-is": { "node_modules/type-is": {
"version": "2.0.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.0.tgz", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
"integrity": "sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"content-type": "^1.0.5", "content-type": "^1.0.5",
@ -6621,15 +6530,6 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
}, },
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/v8-compile-cache-lib": { "node_modules/v8-compile-cache-lib": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",

View file

@ -10,7 +10,7 @@ Index: code-server/lib/vscode/src/vs/base/common/network.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/base/common/network.ts --- code-server.orig/lib/vscode/src/vs/base/common/network.ts
+++ code-server/lib/vscode/src/vs/base/common/network.ts +++ code-server/lib/vscode/src/vs/base/common/network.ts
@@ -232,7 +232,9 @@ class RemoteAuthoritiesImpl { @@ -237,7 +237,9 @@ class RemoteAuthoritiesImpl {
return URI.from({ return URI.from({
scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource, scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource,
authority: `${host}:${port}`, authority: `${host}:${port}`,
@ -99,7 +99,7 @@ Index: code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactor
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts --- code-server.orig/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts
+++ code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts +++ code-server/lib/vscode/src/vs/platform/remote/browser/browserSocketFactory.ts
@@ -281,6 +281,7 @@ export class BrowserSocketFactory implem @@ -282,6 +282,7 @@ export class BrowserSocketFactory implem
connect({ host, port }: WebSocketRemoteConnection, path: string, query: string, debugLabel: string): Promise<ISocket> { connect({ host, port }: WebSocketRemoteConnection, path: string, query: string, debugLabel: string): Promise<ISocket> {
return new Promise<ISocket>((resolve, reject) => { return new Promise<ISocket>((resolve, reject) => {
const webSocketSchema = (/^https:/.test(mainWindow.location.href) ? 'wss' : 'ws'); const webSocketSchema = (/^https:/.test(mainWindow.location.href) ? 'wss' : 'ws');
@ -253,7 +253,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.ts --- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.ts
+++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts +++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
@@ -338,7 +338,8 @@ class LocalStorageURLCallbackProvider ex @@ -339,7 +339,8 @@ class LocalStorageURLCallbackProvider ex
this.startListening(); this.startListening();
} }
@ -263,7 +263,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
} }
private startListening(): void { private startListening(): void {
@@ -583,17 +584,6 @@ class WorkspaceProvider implements IWork @@ -584,17 +585,6 @@ class WorkspaceProvider implements IWork
} }
} }
@ -281,7 +281,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
(function () { (function () {
// Find config by checking for DOM // Find config by checking for DOM
@@ -602,8 +592,8 @@ function readCookie(name: string): strin @@ -604,8 +594,8 @@ function readCookie(name: string): strin
if (!configElement || !configElementAttribute) { if (!configElement || !configElementAttribute) {
throw new Error('Missing web configuration element'); throw new Error('Missing web configuration element');
} }

View file

@ -78,7 +78,7 @@ Index: code-server/lib/vscode/src/vs/platform/environment/common/argv.ts
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/platform/environment/common/argv.ts --- code-server.orig/lib/vscode/src/vs/platform/environment/common/argv.ts
+++ code-server/lib/vscode/src/vs/platform/environment/common/argv.ts +++ code-server/lib/vscode/src/vs/platform/environment/common/argv.ts
@@ -136,6 +136,7 @@ export interface NativeParsedArgs { @@ -137,6 +137,7 @@ export interface NativeParsedArgs {
'disable-chromium-sandbox'?: boolean; 'disable-chromium-sandbox'?: boolean;
sandbox?: boolean; sandbox?: boolean;
'enable-coi'?: boolean; 'enable-coi'?: boolean;

View file

@ -239,8 +239,8 @@ Index: code-server/lib/vscode/src/vs/workbench/services/dialogs/browser/simpleFi
@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService,
@IPathService protected readonly pathService: IPathService, @IPathService protected readonly pathService: IPathService,
@IKeybindingService private readonly keybindingService: IKeybindingService, @IKeybindingService private readonly keybindingService: IKeybindingService,
@@ -310,20 +310,22 @@ export class SimpleFileDialog extends Di @@ -311,20 +311,22 @@ export class SimpleFileDialog extends Di
this.filePickBox.ignoreFocusOut = true; this.filePickBox.placeholder = nls.localize('remoteFileDialog.placeholder', "Folder path");
this.filePickBox.ok = true; this.filePickBox.ok = true;
this.filePickBox.okLabel = typeof this.options.openLabel === 'string' ? this.options.openLabel : this.options.openLabel?.withoutMnemonic; this.filePickBox.okLabel = typeof this.options.openLabel === 'string' ? this.options.openLabel : this.options.openLabel?.withoutMnemonic;
- if ((this.scheme !== Schemas.file) && this.options && this.options.availableFileSystems && (this.options.availableFileSystems.length > 1) && (this.options.availableFileSystems.indexOf(Schemas.file) > -1)) { - if ((this.scheme !== Schemas.file) && this.options && this.options.availableFileSystems && (this.options.availableFileSystems.length > 1) && (this.options.availableFileSystems.indexOf(Schemas.file) > -1)) {
@ -288,7 +288,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/files/browser/views/explo
import { WorkbenchCompressibleAsyncDataTree } from '../../../../../platform/list/browser/listService.js'; import { WorkbenchCompressibleAsyncDataTree } from '../../../../../platform/list/browser/listService.js';
import { ISearchService, QueryType, getExcludes, ISearchConfiguration, ISearchComplete, IFileQuery } from '../../../../services/search/common/search.js'; import { ISearchService, QueryType, getExcludes, ISearchConfiguration, ISearchComplete, IFileQuery } from '../../../../services/search/common/search.js';
import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js';
@@ -1601,7 +1602,8 @@ export class FileDragAndDrop implements @@ -1594,7 +1595,8 @@ export class FileDragAndDrop implements
@IConfigurationService private configurationService: IConfigurationService, @IConfigurationService private configurationService: IConfigurationService,
@IInstantiationService private instantiationService: IInstantiationService, @IInstantiationService private instantiationService: IInstantiationService,
@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService,
@ -298,7 +298,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/files/browser/views/explo
) { ) {
const updateDropEnablement = (e: IConfigurationChangeEvent | undefined) => { const updateDropEnablement = (e: IConfigurationChangeEvent | undefined) => {
if (!e || e.affectsConfiguration('explorer.enableDragAndDrop')) { if (!e || e.affectsConfiguration('explorer.enableDragAndDrop')) {
@@ -1826,15 +1828,17 @@ export class FileDragAndDrop implements @@ -1819,15 +1821,17 @@ export class FileDragAndDrop implements
// External file DND (Import/Upload file) // External file DND (Import/Upload file)
if (data instanceof NativeDragAndDropData) { if (data instanceof NativeDragAndDropData) {

View file

@ -19,7 +19,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
import { renderFormattedText } from '../../../../base/browser/formattedTextRenderer.js'; import { renderFormattedText } from '../../../../base/browser/formattedTextRenderer.js';
import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js';
import { Button } from '../../../../base/browser/ui/button/button.js'; import { Button } from '../../../../base/browser/ui/button/button.js';
@@ -54,7 +54,7 @@ import { IRecentFolder, IRecentWorkspace @@ -53,7 +53,7 @@ import { IRecentFolder, IRecentWorkspace
import { OpenRecentAction } from '../../../browser/actions/windowActions.js'; import { OpenRecentAction } from '../../../browser/actions/windowActions.js';
import { OpenFileFolderAction, OpenFolderAction, OpenFolderViaWorkspaceAction } from '../../../browser/actions/workspaceActions.js'; import { OpenFileFolderAction, OpenFolderAction, OpenFolderViaWorkspaceAction } from '../../../browser/actions/workspaceActions.js';
import { EditorPane } from '../../../browser/parts/editor/editorPane.js'; import { EditorPane } from '../../../browser/parts/editor/editorPane.js';
@ -28,7 +28,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
import { IEditorOpenContext, IEditorSerializer } from '../../../common/editor.js'; import { IEditorOpenContext, IEditorSerializer } from '../../../common/editor.js';
import { IWebviewElement, IWebviewService } from '../../webview/browser/webview.js'; import { IWebviewElement, IWebviewService } from '../../webview/browser/webview.js';
import './gettingStartedColors.js'; import './gettingStartedColors.js';
@@ -872,6 +872,72 @@ export class GettingStartedPage extends @@ -902,6 +902,72 @@ export class GettingStartedPage extends
$('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved")) $('p.subtitle.description', {}, localize({ key: 'gettingStarted.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved"))
); );
@ -101,7 +101,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/welcomeGettingStarted/bro
const leftColumn = $('.categories-column.categories-column-left', {},); const leftColumn = $('.categories-column.categories-column-left', {},);
const rightColumn = $('.categories-column.categories-column-right', {},); const rightColumn = $('.categories-column.categories-column-right', {},);
@@ -907,6 +973,9 @@ export class GettingStartedPage extends @@ -937,6 +1003,9 @@ export class GettingStartedPage extends
recentList.setLimit(5); recentList.setLimit(5);
reset(leftColumn, startList.getDomElement(), recentList.getDomElement()); reset(leftColumn, startList.getDomElement(), recentList.getDomElement());
} }

View file

@ -265,7 +265,7 @@ Index: code-server/lib/vscode/src/server-main.ts
+++ code-server/lib/vscode/src/server-main.ts +++ code-server/lib/vscode/src/server-main.ts
@@ -22,6 +22,9 @@ import { IServerAPI } from './vs/server/ @@ -22,6 +22,9 @@ import { IServerAPI } from './vs/server/
perf.mark('code/server/start'); perf.mark('code/server/start');
(globalThis as any).vscodeServerStartTime = performance.now(); (globalThis as { vscodeServerStartTime?: number }).vscodeServerStartTime = performance.now();
+// This is not indented to make the diff less noisy. We need to move this out +// This is not indented to make the diff less noisy. We need to move this out
+// of the top-level so it will not run immediately and we can control the start. +// of the top-level so it will not run immediately and we can control the start.
@ -279,7 +279,7 @@ Index: code-server/lib/vscode/src/server-main.ts
} }
+} +}
function sanitizeStringArg(val: any): string | undefined { function sanitizeStringArg(val: unknown): string | undefined {
if (Array.isArray(val)) { // if an argument is passed multiple times, minimist creates an array if (Array.isArray(val)) { // if an argument is passed multiple times, minimist creates an array
@@ -283,3 +287,22 @@ function prompt(question: string): Promi @@ -283,3 +287,22 @@ function prompt(question: string): Promi
}); });

View file

@ -32,7 +32,7 @@ Index: code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
+++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts +++ code-server/lib/vscode/src/vs/workbench/browser/web.api.ts
@@ -298,6 +298,11 @@ export interface IWorkbenchConstructionO @@ -298,6 +298,11 @@ export interface IWorkbenchConstructionO
*/ */
readonly configurationDefaults?: Record<string, any>; readonly configurationDefaults?: Record<string, unknown>;
+ /** + /**
+ * Path to the user data directory. + * Path to the user data directory.
@ -79,7 +79,7 @@ Index: code-server/lib/vscode/src/vs/workbench/services/configuration/browser/co
}); });
})); }));
@@ -555,6 +557,12 @@ export class WorkspaceService extends Di @@ -556,6 +558,12 @@ export class WorkspaceService extends Di
previousFolders = this.workspace.folders; previousFolders = this.workspace.folders;
this.workspace.update(workspace); this.workspace.update(workspace);
} else { } else {

View file

@ -83,7 +83,7 @@ Index: code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalE
=================================================================== ===================================================================
--- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts --- code-server.orig/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
+++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ code-server/lib/vscode/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts
@@ -291,7 +291,7 @@ export async function createTerminalEnvi @@ -292,7 +292,7 @@ export async function createTerminalEnvi
// Sanitize the environment, removing any undesirable VS Code and Electron environment // Sanitize the environment, removing any undesirable VS Code and Electron environment
// variables // variables
@ -104,7 +104,7 @@ Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.ts
import type { IURLCallbackProvider } from '../../../workbench/services/url/browser/urlService.js'; import type { IURLCallbackProvider } from '../../../workbench/services/url/browser/urlService.js';
import { create } from '../../../workbench/workbench.web.main.internal.js'; import { create } from '../../../workbench/workbench.web.main.internal.js';
@@ -604,6 +605,39 @@ class WorkspaceProvider implements IWork @@ -606,6 +607,39 @@ class WorkspaceProvider implements IWork
settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined, settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined,
workspaceProvider: WorkspaceProvider.create(config), workspaceProvider: WorkspaceProvider.create(config),
urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute), urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute),

View file

@ -96,7 +96,7 @@ Index: code-server/lib/vscode/src/vs/workbench/api/node/extensionHostProcess.ts
import minimist from 'minimist'; import minimist from 'minimist';
import * as nativeWatchdog from 'native-watchdog'; import * as nativeWatchdog from 'native-watchdog';
import * as net from 'net'; import * as net from 'net';
@@ -449,7 +450,28 @@ async function startExtensionHostProcess @@ -451,7 +452,28 @@ async function startExtensionHostProcess
); );
// rewrite onTerminate-function to be a proper shutdown // rewrite onTerminate-function to be a proper shutdown

View file

@ -94,6 +94,7 @@ export interface UserProvidedArgs extends UserProvidedCodeArgs {
"welcome-text"?: string "welcome-text"?: string
"abs-proxy-base-path"?: string "abs-proxy-base-path"?: string
i18n?: string i18n?: string
"idle-timeout-seconds"?: number
/* Positional arguments. */ /* Positional arguments. */
_?: string[] _?: string[]
} }
@ -303,6 +304,10 @@ export const options: Options<Required<UserProvidedArgs>> = {
path: true, path: true,
description: "Path to JSON file with custom translations. Merges with default strings and supports all i18n keys.", description: "Path to JSON file with custom translations. Merges with default strings and supports all i18n keys.",
}, },
"idle-timeout-seconds": {
type: "number",
description: "Timeout in seconds to wait before shutting down when idle.",
},
} }
export const optionDescriptions = (opts: Partial<Options<Required<UserProvidedArgs>>> = options): string[] => { export const optionDescriptions = (opts: Partial<Options<Required<UserProvidedArgs>>> = options): string[] => {
@ -396,6 +401,10 @@ export const parse = (
throw new Error("--github-auth can only be set in the config file or passed in via $GITHUB_TOKEN") throw new Error("--github-auth can only be set in the config file or passed in via $GITHUB_TOKEN")
} }
if (key === "idle-timeout-seconds" && Number(value) <= 60) {
throw new Error("--idle-timeout-seconds must be greater than 60 seconds.")
}
const option = options[key] const option = options[key]
if (option.type === "boolean") { if (option.type === "boolean") {
;(args[key] as boolean) = true ;(args[key] as boolean) = true
@ -611,6 +620,16 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
args["github-auth"] = process.env.GITHUB_TOKEN args["github-auth"] = process.env.GITHUB_TOKEN
} }
if (process.env.CODE_SERVER_IDLE_TIMEOUT_SECONDS) {
if (isNaN(Number(process.env.CODE_SERVER_IDLE_TIMEOUT_SECONDS))) {
logger.info("CODE_SERVER_IDLE_TIMEOUT_SECONDS must be a number")
}
if (Number(process.env.CODE_SERVER_IDLE_TIMEOUT_SECONDS) <= 60) {
throw new Error("--idle-timeout-seconds must be greater than 60 seconds.")
}
args["idle-timeout-seconds"] = Number(process.env.CODE_SERVER_IDLE_TIMEOUT_SECONDS)
}
// Ensure they're not readable by child processes. // Ensure they're not readable by child processes.
delete process.env.PASSWORD delete process.env.PASSWORD
delete process.env.HASHED_PASSWORD delete process.env.HASHED_PASSWORD

View file

@ -1,5 +1,6 @@
import { logger } from "@coder/logger" import { logger } from "@coder/logger"
import { promises as fs } from "fs" import { promises as fs } from "fs"
import { Emitter } from "../common/emitter"
/** /**
* Provides a heartbeat using a local file to indicate activity. * Provides a heartbeat using a local file to indicate activity.
@ -8,6 +9,9 @@ export class Heart {
private heartbeatTimer?: NodeJS.Timeout private heartbeatTimer?: NodeJS.Timeout
private heartbeatInterval = 60000 private heartbeatInterval = 60000
public lastHeartbeat = 0 public lastHeartbeat = 0
private readonly _onChange = new Emitter<"alive" | "expired" | "unknown">()
readonly onChange = this._onChange.event
private state: "alive" | "expired" | "unknown" = "expired"
public constructor( public constructor(
private readonly heartbeatPath: string, private readonly heartbeatPath: string,
@ -17,6 +21,13 @@ export class Heart {
this.alive = this.alive.bind(this) this.alive = this.alive.bind(this)
} }
private setState(state: typeof this.state) {
if (this.state !== state) {
this.state = state
this._onChange.emit(this.state)
}
}
public alive(): boolean { public alive(): boolean {
const now = Date.now() const now = Date.now()
return now - this.lastHeartbeat < this.heartbeatInterval return now - this.lastHeartbeat < this.heartbeatInterval
@ -28,6 +39,7 @@ export class Heart {
*/ */
public async beat(): Promise<void> { public async beat(): Promise<void> {
if (this.alive()) { if (this.alive()) {
this.setState("alive")
return return
} }
@ -36,7 +48,22 @@ export class Heart {
if (typeof this.heartbeatTimer !== "undefined") { if (typeof this.heartbeatTimer !== "undefined") {
clearTimeout(this.heartbeatTimer) clearTimeout(this.heartbeatTimer)
} }
this.heartbeatTimer = setTimeout(() => heartbeatTimer(this.isActive, this.beat), this.heartbeatInterval)
this.heartbeatTimer = setTimeout(async () => {
try {
if (await this.isActive()) {
this.beat()
} else {
this.setState("expired")
}
} catch (error: unknown) {
logger.warn((error as Error).message)
this.setState("unknown")
}
}, this.heartbeatInterval)
this.setState("alive")
try { try {
return await fs.writeFile(this.heartbeatPath, "") return await fs.writeFile(this.heartbeatPath, "")
} catch (error: any) { } catch (error: any) {
@ -53,20 +80,3 @@ export class Heart {
} }
} }
} }
/**
* Helper function for the heartbeatTimer.
*
* If heartbeat is active, call beat. Otherwise do nothing.
*
* Extracted to make it easier to test.
*/
export async function heartbeatTimer(isActive: Heart["isActive"], beat: Heart["beat"]) {
try {
if (await isActive()) {
beat()
}
} catch (error: unknown) {
logger.warn((error as Error).message)
}
}

View file

@ -11,6 +11,7 @@ import { loadCustomStrings } from "./i18n"
import { register } from "./routes" import { register } from "./routes"
import { VSCodeModule } from "./routes/vscode" import { VSCodeModule } from "./routes/vscode"
import { isDirectory, open } from "./util" import { isDirectory, open } from "./util"
import { wrapper } from "./wrapper"
/** /**
* Return true if the user passed an extension-related VS Code flag. * Return true if the user passed an extension-related VS Code flag.
@ -141,7 +142,7 @@ export const runCodeServer = async (
const app = await createApp(args) const app = await createApp(args)
const protocol = args.cert ? "https" : "http" const protocol = args.cert ? "https" : "http"
const serverAddress = ensureAddress(app.server, protocol) const serverAddress = ensureAddress(app.server, protocol)
const disposeRoutes = await register(app, args) const { disposeRoutes, heart } = await register(app, args)
logger.info(`Using config file ${args.config}`) logger.info(`Using config file ${args.config}`)
logger.info(`${protocol.toUpperCase()} server listening on ${serverAddress.toString()}`) logger.info(`${protocol.toUpperCase()} server listening on ${serverAddress.toString()}`)
@ -166,6 +167,27 @@ export const runCodeServer = async (
logger.info(" - Not serving HTTPS") logger.info(" - Not serving HTTPS")
} }
if (args["idle-timeout-seconds"]) {
logger.info(` - Idle timeout set to ${args["idle-timeout-seconds"]} seconds`)
let idleShutdownTimer: NodeJS.Timeout | undefined
const startIdleShutdownTimer = () => {
idleShutdownTimer = setTimeout(() => {
logger.warn(`Idle timeout of ${args["idle-timeout-seconds"]} seconds exceeded`)
wrapper.exit(0)
}, args["idle-timeout-seconds"]! * 1000)
}
startIdleShutdownTimer()
heart.onChange((state) => {
clearTimeout(idleShutdownTimer)
if (state === "expired") {
startIdleShutdownTimer()
}
})
}
if (args["disable-proxy"]) { if (args["disable-proxy"]) {
logger.info(" - Proxy disabled") logger.info(" - Proxy disabled")
} else if (args["proxy-domain"].length > 0) { } else if (args["proxy-domain"].length > 0) {

View file

@ -28,7 +28,10 @@ import * as vscode from "./vscode"
/** /**
* Register all routes and middleware. * Register all routes and middleware.
*/ */
export const register = async (app: App, args: DefaultedArgs): Promise<Disposable["dispose"]> => { export const register = async (
app: App,
args: DefaultedArgs,
): Promise<{ disposeRoutes: Disposable["dispose"]; heart: Heart }> => {
const heart = new Heart(path.join(paths.data, "heartbeat"), async () => { const heart = new Heart(path.join(paths.data, "heartbeat"), async () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// getConnections appears to not call the callback when there are no more // getConnections appears to not call the callback when there are no more
@ -173,8 +176,11 @@ export const register = async (app: App, args: DefaultedArgs): Promise<Disposabl
app.router.use(errorHandler) app.router.use(errorHandler)
app.wsRouter.use(wsErrorHandler) app.wsRouter.use(wsErrorHandler)
return () => { return {
disposeRoutes: () => {
heart.dispose() heart.dispose()
vscode.dispose() vscode.dispose()
},
heart,
} }
} }

26
test/package-lock.json generated
View file

@ -7,7 +7,7 @@
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@jest-mock/express": "^1.4.5", "@jest-mock/express": "^1.4.5",
"@playwright/test": "^1.46.0", "@playwright/test": "^1.56.1",
"@types/jest": "^27.0.2", "@types/jest": "^27.0.2",
"@types/jsdom": "^16.2.13", "@types/jsdom": "^16.2.13",
"@types/node-fetch": "^2.5.8", "@types/node-fetch": "^2.5.8",
@ -18,7 +18,7 @@
"jest-fetch-mock": "^3.0.3", "jest-fetch-mock": "^3.0.3",
"jsdom": "^16.4.0", "jsdom": "^16.4.0",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
"playwright": "^1.46.0", "playwright": "^1.56.1",
"ts-jest": "^27.0.7", "ts-jest": "^27.0.7",
"wtfnode": "^0.9.1" "wtfnode": "^0.9.1"
} }
@ -1012,13 +1012,13 @@
} }
}, },
"node_modules/@playwright/test": { "node_modules/@playwright/test": {
"version": "1.47.2", "version": "1.56.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.2.tgz", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz",
"integrity": "sha512-jTXRsoSPONAs8Za9QEQdyjFn+0ZQFjCiIztAIF6bi1HqhBzG9Ma7g1WotyiGqFSBRZjIEqMdT8RUlbk1QVhzCQ==", "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright": "1.47.2" "playwright": "1.56.1"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -4305,13 +4305,13 @@
} }
}, },
"node_modules/playwright": { "node_modules/playwright": {
"version": "1.47.2", "version": "1.56.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz",
"integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==", "integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.47.2" "playwright-core": "1.56.1"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -4324,9 +4324,9 @@
} }
}, },
"node_modules/playwright-core": { "node_modules/playwright-core": {
"version": "1.47.2", "version": "1.56.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz", "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz",
"integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==", "integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {

View file

@ -3,7 +3,7 @@
"#": "We must put jest in a sub-directory otherwise VS Code somehow picks up the types and generates conflicts with mocha.", "#": "We must put jest in a sub-directory otherwise VS Code somehow picks up the types and generates conflicts with mocha.",
"devDependencies": { "devDependencies": {
"@jest-mock/express": "^1.4.5", "@jest-mock/express": "^1.4.5",
"@playwright/test": "^1.46.0", "@playwright/test": "^1.56.1",
"@types/jest": "^27.0.2", "@types/jest": "^27.0.2",
"@types/jsdom": "^16.2.13", "@types/jsdom": "^16.2.13",
"@types/node-fetch": "^2.5.8", "@types/node-fetch": "^2.5.8",
@ -14,7 +14,7 @@
"jest-fetch-mock": "^3.0.3", "jest-fetch-mock": "^3.0.3",
"jsdom": "^16.4.0", "jsdom": "^16.4.0",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
"playwright": "^1.46.0", "playwright": "^1.56.1",
"ts-jest": "^27.0.7", "ts-jest": "^27.0.7",
"wtfnode": "^0.9.1" "wtfnode": "^0.9.1"
}, },

View file

@ -1,6 +1,6 @@
import { logger } from "@coder/logger" import { logger } from "@coder/logger"
import { readFile, writeFile, stat, utimes } from "fs/promises" import { readFile, writeFile, stat, utimes } from "fs/promises"
import { Heart, heartbeatTimer } from "../../../src/node/heart" import { Heart } from "../../../src/node/heart"
import { clean, mockLogger, tmpdir } from "../../utils/helpers" import { clean, mockLogger, tmpdir } from "../../utils/helpers"
const mockIsActive = (resolveTo: boolean) => jest.fn().mockResolvedValue(resolveTo) const mockIsActive = (resolveTo: boolean) => jest.fn().mockResolvedValue(resolveTo)
@ -82,7 +82,52 @@ describe("Heart", () => {
}) })
describe("heartbeatTimer", () => { describe("heartbeatTimer", () => {
beforeAll(() => { const testName = "heartbeatTimer"
let testDir = ""
beforeAll(async () => {
await clean(testName)
testDir = await tmpdir(testName)
mockLogger()
})
afterAll(() => {
jest.restoreAllMocks()
})
beforeEach(() => {
jest.useFakeTimers()
})
afterEach(() => {
jest.resetAllMocks()
jest.clearAllTimers()
jest.useRealTimers()
})
it("should call isActive when timeout expires", async () => {
const isActive = true
const mockIsActive = jest.fn().mockResolvedValue(isActive)
const heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive)
await heart.beat()
jest.advanceTimersByTime(60 * 1000)
expect(mockIsActive).toHaveBeenCalled()
})
it("should log a warning when isActive rejects", async () => {
const errorMsg = "oh no"
const error = new Error(errorMsg)
const mockIsActive = jest.fn().mockRejectedValue(error)
const heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive)
await heart.beat()
jest.advanceTimersByTime(60 * 1000)
expect(mockIsActive).toHaveBeenCalled()
expect(logger.warn).toHaveBeenCalledWith(errorMsg)
})
})
describe("stateChange", () => {
const testName = "stateChange"
let testDir = ""
let heart: Heart
beforeAll(async () => {
await clean(testName)
testDir = await tmpdir(testName)
mockLogger() mockLogger()
}) })
afterAll(() => { afterAll(() => {
@ -90,23 +135,28 @@ describe("heartbeatTimer", () => {
}) })
afterEach(() => { afterEach(() => {
jest.resetAllMocks() jest.resetAllMocks()
if (heart) {
heart.dispose()
}
}) })
it("should call beat when isActive resolves to true", async () => { it("should change to alive after a beat", async () => {
const isActive = true heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive(true))
const mockIsActive = jest.fn().mockResolvedValue(isActive) const mockOnChange = jest.fn()
const mockBeatFn = jest.fn() heart.onChange(mockOnChange)
await heartbeatTimer(mockIsActive, mockBeatFn) await heart.beat()
expect(mockIsActive).toHaveBeenCalled()
expect(mockBeatFn).toHaveBeenCalled() expect(mockOnChange.mock.calls[0][0]).toBe("alive")
}) })
it("should log a warning when isActive rejects", async () => { it.only("should change to expired when not active", async () => {
const errorMsg = "oh no" jest.useFakeTimers()
const error = new Error(errorMsg) heart = new Heart(`${testDir}/shutdown.txt`, () => new Promise((resolve) => resolve(false)))
const mockIsActive = jest.fn().mockRejectedValue(error) const mockOnChange = jest.fn()
const mockBeatFn = jest.fn() heart.onChange(mockOnChange)
await heartbeatTimer(mockIsActive, mockBeatFn) await heart.beat()
expect(mockIsActive).toHaveBeenCalled()
expect(mockBeatFn).not.toHaveBeenCalled() await jest.advanceTimersByTime(60 * 1000)
expect(logger.warn).toHaveBeenCalledWith(errorMsg) expect(mockOnChange.mock.calls[1][0]).toBe("expired")
jest.clearAllTimers()
jest.useRealTimers()
}) })
}) })

View file

@ -16,6 +16,7 @@ jest.mock("@coder/logger", () => ({
debug: jest.fn(), debug: jest.fn(),
warn: jest.fn(), warn: jest.fn(),
error: jest.fn(), error: jest.fn(),
named: jest.fn(),
level: 0, level: 0,
}, },
field: jest.fn(), field: jest.fn(),
@ -94,7 +95,7 @@ describe("main", () => {
// Mock routes module // Mock routes module
jest.doMock("../../../src/node/routes", () => ({ jest.doMock("../../../src/node/routes", () => ({
register: jest.fn().mockResolvedValue(jest.fn()), register: jest.fn().mockResolvedValue({ disposeRoutes: jest.fn() }),
})) }))
// Mock loadCustomStrings to succeed // Mock loadCustomStrings to succeed
@ -131,7 +132,7 @@ describe("main", () => {
// Mock routes module // Mock routes module
jest.doMock("../../../src/node/routes", () => ({ jest.doMock("../../../src/node/routes", () => ({
register: jest.fn().mockResolvedValue(jest.fn()), register: jest.fn().mockResolvedValue({ disposeRoutes: jest.fn() }),
})) }))
// Import runCodeServer after mocking // Import runCodeServer after mocking