mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-06-03 06:40:10 +08:00
367 lines
14 KiB
JavaScript
367 lines
14 KiB
JavaScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
|
|
import {
|
|
buildHermesTerminalConfigValues,
|
|
mergeHermesTerminalConfig,
|
|
} from '../scripts/dev-api.js'
|
|
|
|
test('Hermes 终端执行配置读取会提供上游默认值', () => {
|
|
const values = buildHermesTerminalConfigValues({})
|
|
|
|
assert.deepEqual(values, {
|
|
terminalBackend: 'local',
|
|
terminalCwd: '.',
|
|
terminalTimeout: 180,
|
|
terminalLifetimeSeconds: 300,
|
|
terminalShellInitFiles: '',
|
|
terminalAutoSourceBashrc: true,
|
|
terminalPersistentShell: true,
|
|
terminalEnvPassthrough: '',
|
|
terminalDockerMountCwdToWorkspace: false,
|
|
terminalDockerRunAsHostUser: false,
|
|
terminalContainerCpu: 1,
|
|
terminalContainerMemory: 5120,
|
|
terminalContainerDisk: 51200,
|
|
terminalContainerPersistent: true,
|
|
terminalDockerImage: '',
|
|
terminalSingularityImage: '',
|
|
terminalModalImage: '',
|
|
terminalModalMode: 'auto',
|
|
terminalVercelRuntime: 'node24',
|
|
terminalDaytonaImage: '',
|
|
terminalDockerForwardEnv: '',
|
|
terminalDockerEnvJson: '{}',
|
|
terminalDockerVolumes: '',
|
|
terminalDockerExtraArgs: '',
|
|
terminalSshHost: '',
|
|
terminalSshUser: '',
|
|
terminalSshPort: 22,
|
|
terminalSshKey: '',
|
|
})
|
|
})
|
|
|
|
test('Hermes 终端执行配置读取会回显 YAML 字段', () => {
|
|
const values = buildHermesTerminalConfigValues({
|
|
terminal: {
|
|
backend: 'docker',
|
|
cwd: '/workspace',
|
|
timeout: 600,
|
|
lifetime_seconds: 1800,
|
|
shell_init_files: ['~/.zshrc', '${HOME}/.config/hermes/env.sh'],
|
|
auto_source_bashrc: false,
|
|
persistent_shell: false,
|
|
env_passthrough: ['OPENROUTER_API_KEY', 'GITHUB_TOKEN'],
|
|
docker_mount_cwd_to_workspace: true,
|
|
docker_run_as_host_user: true,
|
|
docker_image: 'nikolaik/python-nodejs:python3.11-nodejs20',
|
|
docker_forward_env: ['GITHUB_TOKEN', 'NPM_TOKEN'],
|
|
docker_env: {
|
|
PLAYWRIGHT_BROWSERS_PATH: '/ms-playwright',
|
|
PIP_CACHE_DIR: '/workspace/.cache/pip',
|
|
},
|
|
docker_volumes: ['/data/projects:/workspace/projects', '/data/cache:/cache'],
|
|
docker_extra_args: ['--network=host', '--add-host=host.docker.internal:host-gateway'],
|
|
singularity_image: 'docker://nikolaik/python-nodejs:python3.11-nodejs20',
|
|
modal_image: 'python:3.12',
|
|
modal_mode: 'managed',
|
|
vercel_runtime: 'python3.13',
|
|
daytona_image: 'ubuntu:24.04',
|
|
ssh_host: 'build.example.com',
|
|
ssh_user: 'deploy',
|
|
ssh_port: 2222,
|
|
ssh_key: '~/.ssh/hermes_ed25519',
|
|
container_cpu: 4,
|
|
container_memory: 8192,
|
|
container_disk: 102400,
|
|
container_persistent: false,
|
|
},
|
|
})
|
|
|
|
assert.equal(values.terminalBackend, 'docker')
|
|
assert.equal(values.terminalCwd, '/workspace')
|
|
assert.equal(values.terminalTimeout, 600)
|
|
assert.equal(values.terminalLifetimeSeconds, 1800)
|
|
assert.equal(values.terminalShellInitFiles, '~/.zshrc\n${HOME}/.config/hermes/env.sh')
|
|
assert.equal(values.terminalAutoSourceBashrc, false)
|
|
assert.equal(values.terminalPersistentShell, false)
|
|
assert.equal(values.terminalEnvPassthrough, 'OPENROUTER_API_KEY\nGITHUB_TOKEN')
|
|
assert.equal(values.terminalDockerMountCwdToWorkspace, true)
|
|
assert.equal(values.terminalDockerRunAsHostUser, true)
|
|
assert.equal(values.terminalDockerImage, 'nikolaik/python-nodejs:python3.11-nodejs20')
|
|
assert.equal(values.terminalDockerForwardEnv, 'GITHUB_TOKEN\nNPM_TOKEN')
|
|
assert.equal(values.terminalDockerEnvJson, '{\n "PLAYWRIGHT_BROWSERS_PATH": "/ms-playwright",\n "PIP_CACHE_DIR": "/workspace/.cache/pip"\n}')
|
|
assert.equal(values.terminalDockerVolumes, '/data/projects:/workspace/projects\n/data/cache:/cache')
|
|
assert.equal(values.terminalDockerExtraArgs, '--network=host\n--add-host=host.docker.internal:host-gateway')
|
|
assert.equal(values.terminalSingularityImage, 'docker://nikolaik/python-nodejs:python3.11-nodejs20')
|
|
assert.equal(values.terminalModalImage, 'python:3.12')
|
|
assert.equal(values.terminalModalMode, 'managed')
|
|
assert.equal(values.terminalVercelRuntime, 'python3.13')
|
|
assert.equal(values.terminalDaytonaImage, 'ubuntu:24.04')
|
|
assert.equal(values.terminalSshHost, 'build.example.com')
|
|
assert.equal(values.terminalSshUser, 'deploy')
|
|
assert.equal(values.terminalSshPort, 2222)
|
|
assert.equal(values.terminalSshKey, '~/.ssh/hermes_ed25519')
|
|
assert.equal(values.terminalContainerCpu, 4)
|
|
assert.equal(values.terminalContainerMemory, 8192)
|
|
assert.equal(values.terminalContainerDisk, 102400)
|
|
assert.equal(values.terminalContainerPersistent, false)
|
|
})
|
|
|
|
test('Hermes 终端执行配置保存会保留未知字段并写入上游结构', () => {
|
|
const next = mergeHermesTerminalConfig({
|
|
model: { provider: 'anthropic' },
|
|
terminal: {
|
|
backend: 'local',
|
|
shell_init_files: ['~/.profile'],
|
|
env_passthrough: ['OLD_TOKEN'],
|
|
docker_image: 'custom/python-node',
|
|
docker_forward_env: ['OLD_TOKEN'],
|
|
docker_env: { OLD_FLAG: 'keep-old' },
|
|
docker_volumes: ['/old:/old'],
|
|
docker_extra_args: ['--old'],
|
|
custom_flag: 'keep-terminal',
|
|
},
|
|
streaming: { enabled: true },
|
|
}, {
|
|
terminalBackend: 'docker',
|
|
terminalCwd: '/workspace',
|
|
terminalTimeout: '900',
|
|
terminalLifetimeSeconds: '1200',
|
|
terminalShellInitFiles: '~/.zshrc\n${HOME}/.config/hermes/env.sh\n~/.zshrc',
|
|
terminalAutoSourceBashrc: false,
|
|
terminalPersistentShell: false,
|
|
terminalEnvPassthrough: 'OPENROUTER_API_KEY\nGITHUB_TOKEN\nOPENROUTER_API_KEY',
|
|
terminalDockerMountCwdToWorkspace: true,
|
|
terminalDockerRunAsHostUser: true,
|
|
terminalDockerImage: 'nikolaik/python-nodejs:python3.12-nodejs22',
|
|
terminalDockerForwardEnv: 'GITHUB_TOKEN\nNPM_TOKEN\nGITHUB_TOKEN',
|
|
terminalDockerEnvJson: '{ "PLAYWRIGHT_BROWSERS_PATH": "/ms-playwright", "PIP_CACHE_DIR": "/workspace/.cache/pip" }',
|
|
terminalDockerVolumes: '/data/projects:/workspace/projects\n/data/cache:/cache\n/data/projects:/workspace/projects',
|
|
terminalDockerExtraArgs: '--network=host\n--add-host=host.docker.internal:host-gateway\n--network=host',
|
|
terminalSingularityImage: 'docker://ubuntu:24.04',
|
|
terminalModalImage: 'debian:bookworm',
|
|
terminalModalMode: 'direct',
|
|
terminalVercelRuntime: 'node22',
|
|
terminalDaytonaImage: 'ubuntu:22.04',
|
|
terminalSshHost: 'ssh.example.com',
|
|
terminalSshUser: 'hermes',
|
|
terminalSshPort: '2200',
|
|
terminalSshKey: '~/.ssh/id_ed25519',
|
|
terminalContainerCpu: '2',
|
|
terminalContainerMemory: '6144',
|
|
terminalContainerDisk: '20480',
|
|
terminalContainerPersistent: false,
|
|
})
|
|
|
|
assert.deepEqual(next.model, { provider: 'anthropic' })
|
|
assert.deepEqual(next.streaming, { enabled: true })
|
|
assert.equal(next.terminal.backend, 'docker')
|
|
assert.equal(next.terminal.cwd, '/workspace')
|
|
assert.equal(next.terminal.timeout, 900)
|
|
assert.equal(next.terminal.lifetime_seconds, 1200)
|
|
assert.deepEqual(next.terminal.shell_init_files, ['~/.zshrc', '${HOME}/.config/hermes/env.sh'])
|
|
assert.equal(next.terminal.auto_source_bashrc, false)
|
|
assert.equal(next.terminal.persistent_shell, false)
|
|
assert.deepEqual(next.terminal.env_passthrough, ['OPENROUTER_API_KEY', 'GITHUB_TOKEN'])
|
|
assert.equal(next.terminal.docker_mount_cwd_to_workspace, true)
|
|
assert.equal(next.terminal.docker_run_as_host_user, true)
|
|
assert.equal(next.terminal.docker_image, 'nikolaik/python-nodejs:python3.12-nodejs22')
|
|
assert.equal(next.terminal.singularity_image, 'docker://ubuntu:24.04')
|
|
assert.equal(next.terminal.modal_image, 'debian:bookworm')
|
|
assert.equal(next.terminal.modal_mode, 'direct')
|
|
assert.equal(next.terminal.vercel_runtime, 'node22')
|
|
assert.equal(next.terminal.daytona_image, 'ubuntu:22.04')
|
|
assert.equal(next.terminal.ssh_host, 'ssh.example.com')
|
|
assert.equal(next.terminal.ssh_user, 'hermes')
|
|
assert.equal(next.terminal.ssh_port, 2200)
|
|
assert.equal(next.terminal.ssh_key, '~/.ssh/id_ed25519')
|
|
assert.equal(next.terminal.container_cpu, 2)
|
|
assert.equal(next.terminal.container_memory, 6144)
|
|
assert.equal(next.terminal.container_disk, 20480)
|
|
assert.equal(next.terminal.container_persistent, false)
|
|
assert.deepEqual(next.terminal.docker_forward_env, ['GITHUB_TOKEN', 'NPM_TOKEN'])
|
|
assert.deepEqual(next.terminal.docker_env, {
|
|
PLAYWRIGHT_BROWSERS_PATH: '/ms-playwright',
|
|
PIP_CACHE_DIR: '/workspace/.cache/pip',
|
|
})
|
|
assert.deepEqual(next.terminal.docker_volumes, ['/data/projects:/workspace/projects', '/data/cache:/cache'])
|
|
assert.deepEqual(next.terminal.docker_extra_args, ['--network=host', '--add-host=host.docker.internal:host-gateway'])
|
|
assert.equal(next.terminal.custom_flag, 'keep-terminal')
|
|
})
|
|
|
|
test('Hermes 终端执行配置保存空 Docker 高级字段会删除对应字段', () => {
|
|
const next = mergeHermesTerminalConfig({
|
|
terminal: {
|
|
docker_env: { OLD_FLAG: '1' },
|
|
docker_volumes: ['/old:/old'],
|
|
docker_extra_args: ['--old'],
|
|
custom_flag: 'keep-terminal',
|
|
},
|
|
}, {
|
|
terminalDockerEnvJson: '{}',
|
|
terminalDockerVolumes: ' \n',
|
|
terminalDockerExtraArgs: ' \n',
|
|
})
|
|
|
|
assert.equal(Object.hasOwn(next.terminal, 'docker_env'), false)
|
|
assert.equal(Object.hasOwn(next.terminal, 'docker_volumes'), false)
|
|
assert.equal(Object.hasOwn(next.terminal, 'docker_extra_args'), false)
|
|
assert.equal(next.terminal.custom_flag, 'keep-terminal')
|
|
})
|
|
|
|
test('Hermes 终端执行配置保存空 Docker 环境变量转发会删除对应字段', () => {
|
|
const next = mergeHermesTerminalConfig({
|
|
terminal: {
|
|
docker_forward_env: ['GITHUB_TOKEN'],
|
|
custom_flag: 'keep-terminal',
|
|
},
|
|
}, {
|
|
terminalDockerForwardEnv: ' \n',
|
|
})
|
|
|
|
assert.equal(Object.hasOwn(next.terminal, 'docker_forward_env'), false)
|
|
assert.equal(next.terminal.custom_flag, 'keep-terminal')
|
|
})
|
|
|
|
test('Hermes 终端执行配置保存空 Shell 初始化文件会删除对应字段', () => {
|
|
const next = mergeHermesTerminalConfig({
|
|
terminal: {
|
|
shell_init_files: ['~/.bashrc'],
|
|
custom_flag: 'keep-terminal',
|
|
},
|
|
}, {
|
|
terminalShellInitFiles: ' \n',
|
|
})
|
|
|
|
assert.equal(Object.hasOwn(next.terminal, 'shell_init_files'), false)
|
|
assert.equal(next.terminal.custom_flag, 'keep-terminal')
|
|
})
|
|
|
|
test('Hermes 终端执行配置保存空环境变量透传会删除对应字段', () => {
|
|
const next = mergeHermesTerminalConfig({
|
|
terminal: {
|
|
env_passthrough: ['OPENROUTER_API_KEY'],
|
|
custom_flag: 'keep-terminal',
|
|
},
|
|
}, {
|
|
terminalEnvPassthrough: ' \n',
|
|
})
|
|
|
|
assert.equal(Object.hasOwn(next.terminal, 'env_passthrough'), false)
|
|
assert.equal(next.terminal.custom_flag, 'keep-terminal')
|
|
})
|
|
|
|
test('Hermes 终端执行配置保存空镜像会删除对应字段', () => {
|
|
const next = mergeHermesTerminalConfig({
|
|
terminal: {
|
|
docker_image: 'old-docker',
|
|
singularity_image: 'old-singularity',
|
|
modal_image: 'old-modal',
|
|
daytona_image: 'old-daytona',
|
|
custom_flag: 'keep-terminal',
|
|
},
|
|
}, {
|
|
terminalDockerImage: '',
|
|
terminalSingularityImage: ' ',
|
|
terminalModalImage: '',
|
|
terminalDaytonaImage: ' ',
|
|
})
|
|
|
|
assert.equal(Object.hasOwn(next.terminal, 'docker_image'), false)
|
|
assert.equal(Object.hasOwn(next.terminal, 'singularity_image'), false)
|
|
assert.equal(Object.hasOwn(next.terminal, 'modal_image'), false)
|
|
assert.equal(Object.hasOwn(next.terminal, 'daytona_image'), false)
|
|
assert.equal(next.terminal.custom_flag, 'keep-terminal')
|
|
})
|
|
|
|
test('Hermes 终端执行配置保存空 SSH 字段会删除对应字段', () => {
|
|
const next = mergeHermesTerminalConfig({
|
|
terminal: {
|
|
ssh_host: 'old-host',
|
|
ssh_user: 'old-user',
|
|
ssh_port: 2200,
|
|
ssh_key: '~/.ssh/old',
|
|
custom_flag: 'keep-terminal',
|
|
},
|
|
}, {
|
|
terminalSshHost: '',
|
|
terminalSshUser: ' ',
|
|
terminalSshPort: '22',
|
|
terminalSshKey: '',
|
|
})
|
|
|
|
assert.equal(Object.hasOwn(next.terminal, 'ssh_host'), false)
|
|
assert.equal(Object.hasOwn(next.terminal, 'ssh_user'), false)
|
|
assert.equal(Object.hasOwn(next.terminal, 'ssh_key'), false)
|
|
assert.equal(next.terminal.ssh_port, 22)
|
|
assert.equal(next.terminal.custom_flag, 'keep-terminal')
|
|
})
|
|
|
|
test('Hermes 终端执行配置保存会拒绝非法后端和越界值', () => {
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalBackend: 'unsafe' }),
|
|
/terminal\.backend/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalModalMode: 'unsafe' }),
|
|
/terminal\.modal_mode/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalVercelRuntime: 'ruby' }),
|
|
/terminal\.vercel_runtime/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalTimeout: '0' }),
|
|
/terminal\.timeout/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalLifetimeSeconds: '-1' }),
|
|
/terminal\.lifetime_seconds/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalContainerCpu: '0' }),
|
|
/terminal\.container_cpu/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalContainerMemory: '127' }),
|
|
/terminal\.container_memory/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalSshPort: '0' }),
|
|
/terminal\.ssh_port/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalSshPort: '65536' }),
|
|
/terminal\.ssh_port/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalDockerForwardEnv: 'GOOD_TOKEN\nBAD TOKEN' }),
|
|
/terminal\.docker_forward_env/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalShellInitFiles: 'valid.sh\nbad path.sh' }),
|
|
/terminal\.shell_init_files/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalEnvPassthrough: 'GOOD_TOKEN\nBAD TOKEN' }),
|
|
/terminal\.env_passthrough/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalDockerEnvJson: '[]' }),
|
|
/terminal\.docker_env/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalDockerEnvJson: '{ "BAD KEY": "value" }' }),
|
|
/terminal\.docker_env/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalDockerVolumes: '/host only' }),
|
|
/terminal\.docker_volumes/,
|
|
)
|
|
assert.throws(
|
|
() => mergeHermesTerminalConfig({}, { terminalDockerExtraArgs: 'bad arg' }),
|
|
/terminal\.docker_extra_args/,
|
|
)
|
|
})
|