Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 未发布

- 远程 Claude Docker 槽改用镜像烘焙 kernel 入口(`/opt/kin/kin-kernel`,`REMOTE_KERNEL_ENTRY`),不改 GPT 与本机/KVM 命令;新建或重建容器后生效。(#489、#496)

## 1.3.181 — 2026-10-17

- 采集客户机 CPU、内存、磁盘、MAC、DMI、Shell 与工具链,写入槽位特征并展示于运维页;终端启动打印实时系统特征。
Expand Down
6 changes: 3 additions & 3 deletions src/lib/cluster/remote-slot.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
proxyKey,
} from '../vm/egress.mjs'
import { OS_CATALOG } from '../vm/os-catalog.mjs'
import { resolveKernelDataplane } from '../vm/slot-engine.mjs'
import { REMOTE_KERNEL_ENTRY, resolveKernelDataplane } from '../vm/slot-engine.mjs'
import { isCodexVm } from '../vm/vm-kind.mjs'
import { proxyBlockedReason } from '../vm/proxy-policy.mjs'
import { isKvmRuntime, runtimeKind } from '../vm/runtime-kind.mjs'
Expand All @@ -42,7 +42,6 @@ import {
normalizeTimezone,
KVM_CAPS,
STANDARD_LOCALE,
slotContainerCommand,
writeWorkerFiles,
} from '../vm/vm-runtime.mjs'
import {
Expand Down Expand Up @@ -489,7 +488,8 @@ export function slotContainerBody(vm, { image, network, remoteDir, user, routing
`KIN_VM_NAME=${slotName}`,
`KIN_VM_OS=${vm.kernel}`,
],
Cmd: slotContainerCommand(vm),
// Node images bake the Claude kernel here; local slots bind-mount a different path.
Cmd: codex ? ['sleep', 'infinity'] : [REMOTE_KERNEL_ENTRY, '--gateway-worker', '--config', '/run/kin/kernel.json'],
Labels: {
'kin.vm': '1',
'kin.vm.id': vm.id,
Expand Down
10 changes: 9 additions & 1 deletion test/unit/cluster-placement.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
startSlotImageBuild,
unbindPlacement,
} from '../../src/lib/cluster/placement.mjs'
import { remoteIptablesScript, slotEgressNames } from '../../src/lib/cluster/remote-slot.mjs'
import { remoteIptablesScript, slotContainerBody, slotEgressNames } from '../../src/lib/cluster/remote-slot.mjs'
import {
pullSlotCredentials,
pushSlotCredentials,
Expand Down Expand Up @@ -283,6 +283,14 @@ test('slot image tag follows payload bytes only; a VERSION bump keeps the image'
fs.chmodSync(process.env.KIN_CODEX_KERNEL_BIN, 0o755)

const a = slotImageSpec(root, 'ubuntu-24.04')
const container = slotContainerBody(
{ id: 'vm-03', platform: 'anthropic', node_id: 'node-a', kernel: 'ubuntu-24.04' },
{ image: a.ref, network: 'test-net', remoteDir: '/home/node/vms/vm-03', user: '1000:1000', routing: {} },
)
const entry = a.files.find((file) => `/${file.name}` === container.Cmd[0])
assert.ok(entry, 'the remote Claude command must name a file baked into its slot image')
assert.equal(entry.mode, 0o755)
assert.match(entry.body.toString(), /^#!\/bin\/sh/)
assert.match(a.ref, /^vm2api\/kin-slot-ubuntu-24\.04:[0-9a-f]{12}$/)
const kvm = slotImageSpec(root, 'ubuntu-24.04', { runtime: 'kvm' })
assert.match(kvm.ref, /^vm2api\/kin-slot-kvm-ubuntu-24\.04:[0-9a-f]{12}$/)
Expand Down
35 changes: 31 additions & 4 deletions test/unit/remote-slot-gpt.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import test from 'node:test'
import assert from 'node:assert/strict'
import { SLOT_CAPS_NODE } from '../../src/lib/cluster/placement.mjs'
import { codexRoleMatches, slotContainerBody } from '../../src/lib/cluster/remote-slot.mjs'
import { CODEX_SLOT_ROLE } from '../../src/lib/vm/vm-runtime.mjs'
import { REMOTE_KERNEL_ENTRY } from '../../src/lib/vm/slot-engine.mjs'
import { CODEX_SLOT_ROLE, slotContainerCommand } from '../../src/lib/vm/vm-runtime.mjs'
import { slotHost } from '../../src/lib/vm/slot-host.mjs'
import { startSlotReady } from '../../src/lib/vm/slot-runtime.mjs'

Expand Down Expand Up @@ -30,14 +31,40 @@ test('a remote GPT container idles instead of booting the Claude kernel', () =>
const spec = body(gpt)
assert.deepEqual(spec.Cmd, ['sleep', 'infinity'])
assert.equal(spec.Labels['kin.vm.role'], CODEX_SLOT_ROLE)
const claudeSpec = body(claude)
assert.deepEqual(claudeSpec.Cmd, [
})

for (const runtime of [undefined, { type: 'docker' }]) {
test(`a remote Claude slot uses the baked kernel entry (${runtime?.type || 'default'} runtime)`, () => {
const spec = body({ ...claude, runtime })
assert.equal(REMOTE_KERNEL_ENTRY, '/opt/kin/kin-kernel')
assert.deepEqual(spec.Cmd, [REMOTE_KERNEL_ENTRY, '--gateway-worker', '--config', '/run/kin/kernel.json'])
assert.equal(spec.Labels['kin.vm.role'], undefined)
assert.equal(spec.Labels['kin.vm.runtime'], 'docker')
})
}

test('local Claude commands retain their bind-mounted and wrapped kernel entries', () => {
assert.deepEqual(slotContainerCommand(claude), [
'/usr/local/bin/kin-kernel',
'--gateway-worker',
'--config',
'/run/kin/kernel.json',
])
assert.equal(claudeSpec.Labels['kin.vm.role'], undefined)
assert.deepEqual(slotContainerCommand(claude, { wrapKernel: true }), [
'/home/kincli/.kin/kin-kernel',
'--gateway-worker',
'--config',
'/run/kin/kernel.json',
])
assert.deepEqual(slotContainerCommand(gpt), ['sleep', 'infinity'])
})

test('remote KVM slots retain the runner image command for both platforms', () => {
for (const vm of [claude, gpt]) {
const spec = body({ ...vm, runtime: { type: 'kvm' } })
assert.equal(spec.Labels['kin.vm.runtime'], 'kvm')
assert.equal(Object.hasOwn(spec, 'Cmd'), false)
}
})

test('a remote GPT container still running kin-kernel is recreated', () => {
Expand Down
Loading