12 Commits

Author SHA1 Message Date
jiaojm
6ddd46f191 fix
Some checks failed
backend / cross (aarch64) (push) Successful in 5m5s
backend / cross (arm) (push) Successful in 4m44s
backend / cross (armhf) (push) Successful in 6m54s
backend / cross (i686) (push) Successful in 5m12s
backend / cross (mips) (push) Successful in 5m21s
backend / cross (mips64) (push) Successful in 5m22s
backend / cross (mips64el) (push) Successful in 5m29s
backend / cross (mipsel) (push) Successful in 5m5s
backend / cross (s390x) (push) Successful in 5m26s
backend / cross (win32) (push) Successful in 6m24s
backend / cross (x86_64) (push) Successful in 5m49s
backend / cross (arm) (pull_request) Has been cancelled
backend / cross (armhf) (pull_request) Has been cancelled
backend / cross (i686) (pull_request) Has been cancelled
backend / cross (mips) (pull_request) Has been cancelled
backend / cross (mips64) (pull_request) Has been cancelled
backend / cross (mips64el) (pull_request) Has been cancelled
backend / cross (mipsel) (pull_request) Has been cancelled
backend / cross (s390x) (pull_request) Has been cancelled
backend / cross (win32) (pull_request) Has been cancelled
backend / cross (x86_64) (pull_request) Has been cancelled
backend / cross (aarch64) (pull_request) Has been cancelled
frontend / build (push) Successful in 3m24s
frontend / build (pull_request) Successful in 1m11s
2026-01-12 10:46:02 +08:00
jiaojm
2bd8d90bef fix
Some checks failed
backend / cross (aarch64) (push) Successful in 7m8s
backend / cross (arm) (push) Failing after 1m31s
backend / cross (i686) (push) Has been cancelled
backend / cross (mips) (push) Has been cancelled
backend / cross (mips64) (push) Has been cancelled
backend / cross (mips64el) (push) Has been cancelled
backend / cross (mipsel) (push) Has been cancelled
backend / cross (s390x) (push) Has been cancelled
backend / cross (win32) (push) Has been cancelled
backend / cross (x86_64) (push) Has been cancelled
backend / cross (armhf) (push) Has been cancelled
backend / cross (aarch64) (pull_request) Has been cancelled
backend / cross (arm) (pull_request) Has been cancelled
backend / cross (armhf) (pull_request) Has been cancelled
backend / cross (i686) (pull_request) Has been cancelled
backend / cross (mips) (pull_request) Has been cancelled
backend / cross (mips64) (pull_request) Has been cancelled
backend / cross (mips64el) (pull_request) Has been cancelled
backend / cross (mipsel) (pull_request) Has been cancelled
backend / cross (s390x) (pull_request) Has been cancelled
backend / cross (win32) (pull_request) Has been cancelled
backend / cross (x86_64) (pull_request) Has been cancelled
frontend / build (pull_request) Has been cancelled
2026-01-12 08:57:21 +08:00
jiaojm
c14df5f893 fix 2026-01-11 15:42:42 +08:00
jiaojm
c7bcb552c5 fix 2026-01-10 17:52:57 +08:00
jiaojm
2697717620 fix 2026-01-10 17:09:27 +08:00
jiaojm
44f2244f32 fix 2026-01-10 16:04:28 +08:00
jiaojm
ea1a4c4a52 fix 2026-01-07 18:46:07 +08:00
jiaojm
086096dd3e fix 2026-01-07 18:44:03 +08:00
jiaojm
a9f3ba5ade fix 2026-01-07 18:42:00 +08:00
jiaojm
91ca461202 fix 2026-01-07 18:37:59 +08:00
hwy
92b7d50817 更新 html.h 文件 (#4)
Some checks failed
backend / cross (arm) (push) Failing after 1m57s
backend / cross (aarch64) (push) Failing after 3m5s
backend / cross (armhf) (push) Failing after 2m25s
backend / cross (i686) (push) Failing after 10m24s
backend / cross (mips) (push) Failing after 30s
backend / cross (mips64) (push) Failing after 31s
backend / cross (mips64el) (push) Failing after 31s
backend / cross (mipsel) (push) Failing after 2m52s
backend / cross (s390x) (push) Failing after 31s
backend / cross (win32) (push) Failing after 31s
backend / cross (x86_64) (push) Failing after 31s
docker / build (push) Failing after 1m31s
Co-authored-by: 孟宁 <mengning@mengning.com.cn>
Reviewed-on: #4
Co-authored-by: hwy <1093970372@qq.com>
Co-committed-by: hwy <1093970372@qq.com>
2026-01-06 01:29:17 +00:00
44a0826a7d Merge pull request '重构开发容器创建过程' (#3) from fix/issue-32 into main
Some checks failed
docker / build (push) Failing after 2m44s
Reviewed-on: #3
2026-01-05 02:19:46 +00:00
6 changed files with 16319 additions and 16395 deletions

View File

@@ -23,7 +23,7 @@ jobs:
matrix:
target: [i686, x86_64, arm, armhf, aarch64, mips, mipsel, mips64, mips64el, s390x, win32]
steps:
- uses: actions/checkout@v4
- uses: https://devstar.cn/actions/checkout@v4
- name: Install packages
run: |
sudo apt-get update
@@ -32,8 +32,3 @@ jobs:
env:
BUILD_TARGET: ${{ matrix.target }}
run: ./scripts/cross-build.sh
- uses: actions/upload-artifact@v4
with:
name: ttyd.${{ matrix.target }}
path: build/ttyd*

View File

@@ -12,7 +12,7 @@ jobs:
packages: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: https://devstar.cn/actions/checkout@v4
- name: Install packages
run: |
sudo apt-get update
@@ -25,32 +25,37 @@ jobs:
[ "$arch" = "armv7" ] && arch="arm"
mkdir -p dist/$arch && cp build/ttyd dist/$arch/ttyd
done
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
- uses: https://devstar.cn/alexios/setup-qemu-action@v3
- uses: https://devstar.cn/alexios/setup-buildx-action@v3
- uses: https://devstar.cn/alexios/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- uses: docker/login-action@v3
- uses: alexios/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Determine docker tags
id: docker_tag
env:
DOCKER_USER: ${{ vars.DOCKER_HUB_USERNAME }}
PROJECT: ${{ vars.PROJECT_NAME }}
run: |
FULL_IMAGE_NAME="${DOCKER_USER}/${PROJECT}"
case $GITHUB_REF in
refs/tags/*)
TAG_NAME=${GITHUB_REF#refs/tags/}
echo "DOCKER_TAG=tsl0922/ttyd:${TAG_NAME}" >> $GITHUB_ENV
echo "ALPINE_TAG=tsl0922/ttyd:${TAG_NAME}-alpine" >> $GITHUB_ENV
echo "DOCKER_TAG=${FULL_IMAGE_NAME}:${TAG_NAME}" >> $GITHUB_ENV
echo "ALPINE_TAG=${FULL_IMAGE_NAME}:${TAG_NAME}-alpine" >> $GITHUB_ENV
;;
*)
echo "DOCKER_TAG=tsl0922/ttyd:latest" >> $GITHUB_ENV
echo "ALPINE_TAG=tsl0922/ttyd:alpine" >> $GITHUB_ENV
echo "DOCKER_TAG=${FULL_IMAGE_NAME}:latest" >> $GITHUB_ENV
echo "ALPINE_TAG=${FULL_IMAGE_NAME}:alpine" >> $GITHUB_ENV
esac
- name: build/push docker image
uses: docker/build-push-action@v6
uses: https://devstar.cn/alexios/build-push-action@v6
with:
context: .
file: ./Dockerfile
@@ -60,7 +65,7 @@ jobs:
${{ env.DOCKER_TAG }}
ghcr.io/${{ env.DOCKER_TAG }}
- name: build/push docker image (alpine)
uses: docker/build-push-action@v6
uses: https://devstar.cn/alexios/build-push-action@v6
with:
context: .
file: ./Dockerfile.alpine

View File

@@ -14,15 +14,16 @@ jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: https://devstar.cn/actions/checkout@v4
- uses: https://devstar.cn/actions/setup-node@v4
with:
node-version: 18
- name: Run yarn install, check and build
run: |
corepack enable
corepack prepare yarn@stable --activate
yarn install
yarn install --no-immutable
yarn add -D eslint-plugin-n
yarn run check
yarn run build
working-directory: html

View File

@@ -112,7 +112,6 @@ export class Xterm {
private containerStatus = "";
private attachCommandSent = false;
private attachCommandSentAt?: number;
private ptyOutputReceived = false;
constructor(
private options: XtermOptions,
private sendCb: () => void
@@ -277,7 +276,6 @@ export class Xterm {
@bind
public connect() {
this.socket = new WebSocket(this.options.wsUrl, ['tty']);
this.ptyOutputReceived = false;
const { socket, register } = this;
socket.binaryType = 'arraybuffer';
@@ -312,7 +310,6 @@ export class Xterm {
this.doReconnect = this.reconnect;
this.initListeners();
terminal.focus();
this.tryExecuteAttachCommand();
}
@bind
@@ -343,43 +340,6 @@ export class Xterm {
}
}
@bind
private tryExecuteAttachCommand() {
console.log('[Xterm] tryExecuteAttachCommand called:', {
attachCommandSent: this.attachCommandSent,
connectStatus: this.connectStatus,
hasCommand: !!(this.postAttachCommand && this.postAttachCommand.length > 0),
socketReady: this.socket?.readyState === WebSocket.OPEN,
ptyOutputReceived: this.ptyOutputReceived
});
if (this.attachCommandSent || this.connectStatus) {
console.log('[Xterm] Skipping: command already sent or connected');
return;
}
if (!this.postAttachCommand || this.postAttachCommand.length === 0) {
console.log('[Xterm] Skipping: no command available');
return;
}
if (this.socket?.readyState !== WebSocket.OPEN) {
console.log('[Xterm] Skipping: WebSocket not ready, state:', this.socket?.readyState);
return;
}
if (!this.ptyOutputReceived) {
console.log('[Xterm] Skipping: ttyd not ready yet (waiting for first output)');
return; // Wait for TTY readiness confirm via output
}
const cmd = this.postAttachCommand[0];
if (cmd) {
console.log('[Xterm] ✅ All conditions met, executing attach command...');
this.sendData(cmd + "\n");
this.attachCommandSent = true;
this.attachCommandSentAt = Date.now();
console.log('[Xterm] Command sent at:', new Date(this.attachCommandSentAt).toISOString());
}
}
/**
* 获取 URL 查询参数
*/
@@ -433,8 +393,14 @@ export class Xterm {
}
// 执行连接容器的命令(只执行一次)
this.postAttachCommand = data.command.split('\n');
this.tryExecuteAttachCommand();
const parts = data.command.split('\n');
if (parts[0] && !this.connectStatus) {
console.log('[Xterm] Successfully loaded connection command, executing...');
this.sendData(parts[0]+"\n");
this.attachCommandSent = true;
this.attachCommandSentAt = Date.now();
}
this.postAttachCommand = parts;
})
.catch(error => {
console.error(`[Xterm] Error loading command (attempt ${retryCount + 1}/${maxRetries}):`, error);
@@ -499,66 +465,48 @@ export class Xterm {
const data = rawData.slice(1);
switch (cmd) {
case Command.OUTPUT:
if (!this.ptyOutputReceived) {
this.ptyOutputReceived = true;
console.log('[Xterm] ✅ ttyd is now ready (received first output), attempting to execute attach command');
this.tryExecuteAttachCommand();
}
const decodedData = textDecoder.decode(data);
console.log('[ttyd] output:', decodedData);
const compactOutput = decodedData.replace(/\s/g, '');
const { options } = this.getUrlParams();
if (options.get('type') === 'docker') {
// 保存host的标题
const pureContent = decodedData.replace(/\u001B\[[0-9;?]*[ -\/]*[@-~]/g, '').replace(/\u0007/g, '').trim();
if (this.hostTitle === '' && pureContent.length > 0){
if (this.hostTitle === ''){
this.hostTitle = compactOutput;
console.log('[Xterm] Host title captured:', this.hostTitle);
}
// 检测是否退出devcontainer标题等于host的标题
if (this.connectStatus && this.hostTitle && compactOutput.includes(this.hostTitle)){
console.log('[Xterm] Detected exit to host shell');
if (this.connectStatus && compactOutput.includes(this.hostTitle)){
this.connectStatus = false;
this.connectionMessageBuffer = '';
this.attachCommandSent = false;
this.attachCommandSentAt = undefined;
this.postAttachCommandStatus = false;
this.ptyOutputReceived = false; // 重置 PTY 状态
}
if (this.connectStatus) {
try {
this.writeFunc(data);
} catch (e) {
console.error('[Xterm] writeFunc error:', e);
}
} else {
// 未连接状态:缓冲所有输出
this.connectionMessageBuffer += decodedData;
const successMarker = 'Successfully connected to the devcontainer';
// 尝试在 buffer 中查找成功标记
const markerIndex = this.connectionMessageBuffer.indexOf(successMarker);
if (markerIndex !== -1) {
console.log('[Xterm] Connection established, flushing buffer.');
// 检测连接完成:监听 "Successfully connected to the devcontainer" 消息
// 这条消息是由连接命令中的 echo "$WEB_TERMINAL_HELLO" 输出的
if (!this.connectStatus) {
const sanitizedOutput = this.stripAnsi(decodedData).replace(/\r/g, '\n');
const combinedOutput = this.connectionMessageBuffer + sanitizedOutput;
const segments = combinedOutput.split(/\n/);
this.connectionMessageBuffer = segments.pop() ?? '';
const hasSuccessLine = segments.some(line => line.trim() === 'Successfully connected to the devcontainer');
if (hasSuccessLine) {
this.connectStatus = true;
this.terminal.options.disableStdin = false;
const validOutput = this.connectionMessageBuffer.substring(markerIndex);
this.writeData(validOutput);
this.connectionMessageBuffer = '';
}
if (this.connectionMessageBuffer.length > 20000) {
console.warn('[Xterm] Buffer overflow protection. Flushing all.');
this.writeData(this.connectionMessageBuffer);
this.connectionMessageBuffer = '';
this.attachCommandSentAt = undefined;
console.log('[Xterm] Connection established, enabling terminal input');
// 确保终端输入已启用
this.terminal.options.disableStdin = false;
}
}
// 连接完成之前,过滤掉 docker exec 命令的标题输出ANSI 码和 docker-H 开头的输出)
if (
!(this.connectStatus === false &&
(textDecoder.decode(data).includes('\x1b') ||
textDecoder.decode(data).replace(/\s/g, '').includes('docker-H')))
){
this.writeFunc(data);
}
// 连接完成且出现容器的标题且没有执行过postAttach命令
if (this.connectStatus && compactOutput.includes(this.workdir) && !this.postAttachCommandStatus){
console.log('[Xterm] Detected workdir in output, executing postAttachCommand');

View File

@@ -16,9 +16,17 @@ MBEDTLS_VERSION="${MBEDTLS_VERSION:-2.28.5}"
LIBUV_VERSION="${LIBUV_VERSION:-1.44.2}"
LIBWEBSOCKETS_VERSION="${LIBWEBSOCKETS_VERSION:-4.3.3}"
BASE_URL="https://devstar.cn/alexios/zip-repo/releases/download"
URL_TOOLCHAIN="${BASE_URL}/v1.0/aarch64-linux-musl-cross.tgz"
URL_ZLIB="${BASE_URL}/v1.0/zlib-1.3.1.tar.gz"
URL_JSONC="${BASE_URL}/v1.0/json-c-0.17.tar.gz"
URL_MBEDTLS="${BASE_URL}/v1.0/mbedtls-2.28.5.tar.gz"
URL_LIBUV="${BASE_URL}/v1.0/libuv-v1.44.2.tar.gz"
URL_LWS="${BASE_URL}/v1.0/libwebsockets-4.3.3.tar.gz"
build_zlib() {
echo "=== Building zlib-${ZLIB_VERSION} (${TARGET})..."
curl -fSsLo- "https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
curl -kfSsLo- "$URL_ZLIB" | tar xz -C "${BUILD_DIR}"
pushd "${BUILD_DIR}"/zlib-"${ZLIB_VERSION}"
env CHOST="${TARGET}" ./configure --static --archs="-fPIC" --prefix="${STAGE_DIR}"
make -j"$(nproc)" install
@@ -27,7 +35,7 @@ build_zlib() {
build_json-c() {
echo "=== Building json-c-${JSON_C_VERSION} (${TARGET})..."
curl -fSsLo- "https://s3.amazonaws.com/json-c_releases/releases/json-c-${JSON_C_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
curl -kfSsLo- "$URL_JSONC" | tar xz -C "${BUILD_DIR}"
pushd "${BUILD_DIR}/json-c-${JSON_C_VERSION}"
rm -rf build && mkdir -p build && cd build
cmake -DCMAKE_TOOLCHAIN_FILE="${BUILD_DIR}/cross-${TARGET}.cmake" \
@@ -43,7 +51,7 @@ build_json-c() {
build_mbedtls() {
echo "=== Building mbedtls-${MBEDTLS_VERSION} (${TARGET})..."
curl -fSsLo- "https://github.com/ARMmbed/mbedtls/archive/v${MBEDTLS_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
curl -kfSsLo- "$URL_MBEDTLS" | tar xz -C "${BUILD_DIR}"
pushd "${BUILD_DIR}/mbedtls-${MBEDTLS_VERSION}"
rm -rf build && mkdir -p build && cd build
cmake -DCMAKE_TOOLCHAIN_FILE="${BUILD_DIR}/cross-${TARGET}.cmake" \
@@ -57,7 +65,7 @@ build_mbedtls() {
build_libuv() {
echo "=== Building libuv-${LIBUV_VERSION} (${TARGET})..."
curl -fSsLo- "https://dist.libuv.org/dist/v${LIBUV_VERSION}/libuv-v${LIBUV_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
curl -kfSsLo- "$URL_LIBUV" | tar xz -C "${BUILD_DIR}"
pushd "${BUILD_DIR}/libuv-v${LIBUV_VERSION}"
./autogen.sh
env CFLAGS=-fPIC ./configure --disable-shared --enable-static --prefix="${STAGE_DIR}" --host="${TARGET}"
@@ -83,7 +91,7 @@ EOF
build_libwebsockets() {
echo "=== Building libwebsockets-${LIBWEBSOCKETS_VERSION} (${TARGET})..."
curl -fSsLo- "https://github.com/warmcat/libwebsockets/archive/v${LIBWEBSOCKETS_VERSION}.tar.gz" | tar xz -C "${BUILD_DIR}"
curl -kfSsLo- "$URL_LWS" | tar xz -C "${BUILD_DIR}"
pushd "${BUILD_DIR}/libwebsockets-${LIBWEBSOCKETS_VERSION}"
sed -i 's/ websockets_shared//g' cmake/libwebsockets-config.cmake.in
sed -i 's/ OR PC_OPENSSL_FOUND//g' lib/tls/CMakeLists.txt
@@ -134,7 +142,6 @@ build() {
ALIAS="$2"
STAGE_DIR="${STAGE_ROOT}/${TARGET}"
BUILD_DIR="${BUILD_ROOT}/${TARGET}"
MUSL_CC_URL="https://github.com/tsl0922/musl-toolchains/releases/download/2021-11-23"
COMPONENTS="1"
SYSTEM="Linux"
@@ -144,12 +151,12 @@ build() {
fi
echo "=== Installing toolchain ${ALIAS} (${TARGET})..."
mkdir -p "${CROSS_ROOT}" && export PATH="${PATH}:${CROSS_ROOT}/bin"
curl -fSsLo- "${MUSL_CC_URL}/${TARGET}-cross.tgz" | tar xz -C "${CROSS_ROOT}" --strip-components=${COMPONENTS}
echo "=== Building target ${ALIAS} (${TARGET})..."
TOOLCHAIN_FILE="${TARGET}-cross.tgz"
DOWNLOAD_URL="${BASE_URL}/v1.0/${TOOLCHAIN_FILE}"
echo "Downloading toolchain: $DOWNLOAD_URL"
curl -kfSsLo- "$DOWNLOAD_URL" | tar xz -C "${CROSS_ROOT}" --strip-components=${COMPONENTS}
rm -rf "${STAGE_DIR}" "${BUILD_DIR}"
mkdir -p "${STAGE_DIR}" "${BUILD_DIR}"
export PKG_CONFIG_PATH="${STAGE_DIR}/lib/pkgconfig"

32530
src/html.h generated

File diff suppressed because it is too large Load Diff