fix: project runner skills into dev containers

This commit is contained in:
Codex
2026-05-24 03:08:21 +00:00
parent 99e91c1c83
commit 9145c063f9
10 changed files with 112 additions and 7 deletions
+2 -2
View File
@@ -238,13 +238,13 @@ bun scripts/code-queue-pr-preflight-example.ts --repo pikasTech/unidesk --base m
### Runner Skill 可用性
D601 Code Queue runner 的长期 skills source of truth 是宿主 `/home/ubuntu/.agents/skills`,生产和 dev Code Queue Pod 都必须只读挂载到容器内 `/root/.agents/skills`,并设置 `UNIDESK_SKILLS_PATH=/root/.agents/skills`如果 target 暂缺但 approved source 存在且必需 skill 可读,runtime 必须把 `UNIDESK_SKILLS_PATH` 解析为可用 source 传给 runner,并在 health/preflight 标记 `runnerUsable=true``contractOk=false``resolvedPathSource=source-fallback``degradedReason=skills-target-missing``resolution.hostRolloutRequired=true`;这表示任务执行能力可用,但 hostPath 投影仍需 host rollout 修复。不要使用或传播任何拼错的 skills 路径;如果配置的 source 或 target 命中拼写错误路径,health/preflight 必须直接返回 `blocker=forbidden-skills-path-configured`,并在 `pathSpelling.forbiddenPathConfigured=true``pathSpelling.forbiddenPathRoles` 中标明是 source、target 或两者,而不是降级成普通 missing path。
D601 Code Queue runner 的长期 skills source of truth 是宿主 `/home/ubuntu/.agents/skills`,生产和 dev Code Queue Pod 都必须只读挂载到容器内 `/root/.agents/skills`,并设置 `UNIDESK_SKILLS_PATH=/root/.agents/skills`provider dev container 由 scheduler 通过 Docker 启动时,也必须把同一宿主 source 只读 bind 到同一 target,并把 `UNIDESK_SKILLS_PATH` 显式传给 Codex/OpenCode 进程。target 缺失时即使 approved source 可读也不能 fallback 给 runnerhealth/preflight 必须标记 `runnerUsable=false``contractOk=false``resolvedPathSource=missing``degradedReason=skills-target-missing``resolution.hostRolloutRequired=true`。不要使用或传播任何拼错的 skills 路径;如果配置的 source 或 target 命中拼写错误路径,health/preflight 必须直接返回 `blocker=forbidden-skills-path-configured`,并在 `pathSpelling.forbiddenPathConfigured=true``pathSpelling.forbiddenPathRoles` 中标明是 source、target 或两者,而不是降级成普通 missing path。
执行面 `/health``/api/dev-ready``/api/runtime-preflight` 必须输出同一份只读 skill availability report。稳定字段包括 `source``target``resolvedPath``resolvedPathSource``resolution``requiredSkills``missingSkills``sourceSkillCount``targetSkillCount``degraded``degradedReason``blocker``pathSpelling``valuesPrinted=false``requiredSkills` 至少覆盖 `docs-spec``cli-spec``frontend-design``playwright-cli`;如果 source 与 target 都缺失、必需 skill 均不可用、拼写错误路径被配置或拼写错误路径已存在,报告必须显示 `ok=false` 和结构化 `blocker`,不能把 runner 能力缺口伪装成业务任务失败。target symlink 到 approved source 属于可接受的兼容修复,必须显示 `resolvedPathSource=target-symlink` 和有界 skill count。
执行面还必须提供 dry-run skills sync/preflight 合同:稳定入口是 `GET /api/skills-sync?dryRun=1`CLI 入口是 `bun scripts/cli.ts codex skills-sync --dry-run [--full]`。该合同只描述受控 hostPath 生命周期,不复制文件、不从任意路径静默加载、不重启服务、不 rollout Pod、不读取 Secret。默认输出保持紧凑,必须报告 source、target、expected env/mount、required skill 列表、source/target skill counts、missing source/target skills、permission failure count、plannedActions 和修复指令;逐 skill 细节、完整 permission failure 和原始报告只能通过 `--full` 显式展开。非 dry-run 请求必须失败。
受控生命周期是更新宿主 `/home/ubuntu/.agents/skills` 这一 approved source,然后让生产和 dev Code Queue Pod 通过 manifest 中的 read-only hostPath 挂载读取 `/root/.agents/skills`。在线热更新只能作为临时恢复手段,长期验收必须以 manifest/source-of-truth、dry-run sync contract、结构化 health/preflight 和合同测试为准。需要验证时优先运行:
受控生命周期是更新宿主 `/home/ubuntu/.agents/skills` 这一 approved source,然后让生产和 dev Code Queue Pod 通过 manifest 中的 read-only hostPath 挂载读取 `/root/.agents/skills`provider dev container 还必须通过启动脚本把同一 source bind 到同一 target。在线热更新只能作为临时恢复手段,长期验收必须以 manifest/source-of-truth、runner container bind、dry-run sync contract、结构化 health/preflight 和合同测试为准。需要验证时优先运行:
```bash
bun scripts/cli.ts codex skills-sync --dry-run
@@ -3,6 +3,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import { spawnSync } from "node:child_process";
import { collectSkillAvailability, collectSkillSyncPreflight } from "../src/components/microservices/code-queue/src/skill-availability";
import { buildDevContainerPlan, configureProviderRuntime, providerRuntimeForTest } from "../src/components/microservices/code-queue/src/provider-runtime";
import { codexPrPreflightQueryForTest } from "./src/code-queue";
import { summarizeMicroserviceObservation } from "./src/microservices";
@@ -71,6 +72,9 @@ assertCondition(!docsReference.includes(forbiddenPathLiteral), "reference docs m
assertCondition(countOccurrences(productionManifest, "name: UNIDESK_SKILLS_PATH") === 3, "production read/write/scheduler must set UNIDESK_SKILLS_PATH", {
count: countOccurrences(productionManifest, "name: UNIDESK_SKILLS_PATH"),
});
assertCondition(countOccurrences(productionManifest, "name: CODE_QUEUE_RUNNER_SKILLS_SOURCE_PATH") === 3, "production read/write/scheduler must expose the approved runner skills source path", {
count: countOccurrences(productionManifest, "name: CODE_QUEUE_RUNNER_SKILLS_SOURCE_PATH"),
});
assertCondition(countOccurrences(productionManifest, "mountPath: /root/.agents/skills") === 3, "production read/write/scheduler must mount skills target", {
count: countOccurrences(productionManifest, "mountPath: /root/.agents/skills"),
});
@@ -81,6 +85,9 @@ assertCondition(countOccurrences(productionManifest, "name: skills-dir") >= 6, "
count: countOccurrences(productionManifest, "name: skills-dir"),
});
assertCondition(devManifest.includes("path: /home/ubuntu/.agents/skills"), "dev manifest should keep the same hostPath source of truth");
assertCondition(countOccurrences(devManifest, "name: CODE_QUEUE_RUNNER_SKILLS_SOURCE_PATH") === 3, "dev read/write/scheduler must expose the approved runner skills source path", {
count: countOccurrences(devManifest, "name: CODE_QUEUE_RUNNER_SKILLS_SOURCE_PATH"),
});
const devCodeQueueDeploy = (deployJson.environments?.dev?.services ?? []).find((service) => service.id === "code-queue");
assertCondition(devCodeQueueDeploy !== undefined, "deploy.json dev environment must include code-queue");
@@ -107,6 +114,71 @@ assertCondition(pinnedRuntimePreflight.includes("skills.contractOk && ports.code
assertCondition(pinnedIndexSource.includes("skills.contractOk === true"), "deploy.json dev code-queue commit dev-ready must require target projection contract");
assertCondition(pinnedIndexSource.includes("return config.skillsPath"), "deploy.json dev code-queue commit must keep runner UNIDESK_SKILLS_PATH on the configured target");
configureProviderRuntime({
config: {
codexHome: "/var/lib/unidesk/code-queue/codex-home",
defaultWorkdir: "/workspace",
devContainerDefaultProviderId: "D601",
devContainerImage: "unidesk-code-queue:d601",
devContainerMasterHost: "74.48.78.17",
devContainerWorkdir: "/home/ubuntu",
executionProviderIds: ["D601"],
mainProviderId: "D601-main",
remoteCodexEnvKeys: [],
remoteDefaultWorkdir: "/home/ubuntu",
runnerSkillsSourcePath: "/home/ubuntu/.agents/skills",
skillsPath: "/root/.agents/skills",
sourceCodexConfig: "/root/.codex/config.toml",
windowsNativeCodexBridgeDir: "/home/ubuntu/.unidesk/code-queue/windows-native-codex",
windowsNativeCodexCommand: "codex app-server --listen stdio://",
windowsNativeCodexConnectHost: "host.docker.internal",
windowsNativeCodexDefaultWorkdir: "/mnt/f/Work/ConStart",
windowsNativeCodexIdleTimeoutMs: 600_000,
},
safePreview: (value: string, max = 1000) => value.slice(0, max),
});
const devContainerPlan = buildDevContainerPlan("D601", { workdir: "/home/ubuntu" });
const devContainerStartScript = providerRuntimeForTest.remoteContainerStartScript(devContainerPlan, false);
assertCondition(devContainerStartScript.includes("SKILLS_SOURCE='/home/ubuntu/.agents/skills'"), "provider dev container start must use the D601 host skills source", devContainerStartScript);
assertCondition(devContainerStartScript.includes("SKILLS_TARGET='/root/.agents/skills'"), "provider dev container start must use the runner target skills path", devContainerStartScript);
assertCondition(devContainerStartScript.includes('-v "$SKILLS_SOURCE":"$SKILLS_TARGET":ro'), "provider dev container must bind source skills read-only to target", devContainerStartScript);
assertCondition(devContainerStartScript.includes('-e UNIDESK_SKILLS_PATH="$SKILLS_TARGET"'), "provider dev container must export UNIDESK_SKILLS_PATH in docker run", devContainerStartScript);
assertCondition(devContainerStartScript.includes('test -r "$UNIDESK_SKILLS_PATH/docs-spec/SKILL.md"'), "provider dev container readiness must verify required docs-spec skill at target", devContainerStartScript);
assertCondition(devContainerStartScript.includes('test -r "$UNIDESK_SKILLS_PATH/cli-spec/SKILL.md"'), "provider dev container readiness must verify required cli-spec skill at target", devContainerStartScript);
assertCondition(devContainerStartScript.includes('test -r "$UNIDESK_SKILLS_PATH/frontend-design/SKILL.md"'), "provider dev container readiness must verify required frontend-design skill at target", devContainerStartScript);
assertCondition(devContainerStartScript.includes("playwright-cli/SKILL.md") && devContainerStartScript.includes("playwright/SKILL.md"), "provider dev container readiness must accept the playwright-cli alias", devContainerStartScript);
assertCondition(devContainerStartScript.includes("reuse_ready") && devContainerStartScript.includes('test "$UNIDESK_SKILLS_PATH" = "/root/.agents/skills"'), "provider dev container reuse must revalidate target skills before keeping an old container", devContainerStartScript);
const remoteCodexCommand = providerRuntimeForTest.remoteAppServerCommand({
id: "task",
queueId: "default",
prompt: "",
status: "running",
cwd: "/home/ubuntu",
providerId: "D601",
model: "gpt-5.5",
executionMode: "default",
currentAttempt: 1,
attempts: 1,
maxAttempts: 1,
codexThreadId: null,
reasoningEffort: null,
createdAt: "2026-05-24T00:00:00.000Z",
updatedAt: "2026-05-24T00:00:00.000Z",
startedAt: "2026-05-24T00:00:00.000Z",
completedAt: null,
lastActivityAt: "2026-05-24T00:00:00.000Z",
branch: null,
commitSha: null,
title: null,
error: null,
judge: null,
outputSeq: 0,
outputs: [],
events: [],
metadata: {},
} as never);
assertCondition(remoteCodexCommand.includes("export UNIDESK_SKILLS_PATH=") && remoteCodexCommand.includes("/root/.agents/skills"), "remote codex app-server must receive the target skills env", remoteCodexCommand);
const available = collectSkillAvailability({
source: "/home/ubuntu/.agents/skills",
target: "/home/ubuntu/.agents/skills",
@@ -446,6 +518,7 @@ process.stdout.write(`${JSON.stringify({
ok: true,
checks: [
"production Code Queue mounts /home/ubuntu/.agents/skills read-only at /root/.agents/skills",
"provider dev containers bind /home/ubuntu/.agents/skills read-only at /root/.agents/skills and pass UNIDESK_SKILLS_PATH to Codex/OpenCode",
"deploy.json dev Code Queue pins a commit whose manifest and runtime require the target skills projection",
"skill availability report exposes source, target, requiredSkills, missingSkills, version fingerprint/mtime, degraded/blocker and valuesPrinted=false",
"skills sync dry-run reports source, target, counts, version fingerprint/mtime, missing skills, permission failures, instructions and no-copy actions",
@@ -24,6 +24,7 @@ services:
CODE_QUEUE_STARTUP_OA_BACKFILL_ENABLED: "${CODE_QUEUE_STARTUP_OA_BACKFILL_ENABLED:-false}"
CODE_QUEUE_WORKDIR: "${CODE_QUEUE_WORKDIR:-/workspace}"
UNIDESK_SKILLS_PATH: "${UNIDESK_SKILLS_PATH:-/root/.agents/skills}"
CODE_QUEUE_RUNNER_SKILLS_SOURCE_PATH: "${CODE_QUEUE_RUNNER_SKILLS_SOURCE_PATH:-/home/ubuntu/.agents/skills}"
CODE_QUEUE_CODEX_HOME: "/var/lib/unidesk/code-queue/codex-home"
CODE_QUEUE_OPENCODE_XDG_DIR: "/var/lib/unidesk/code-queue/opencode-xdg"
CODE_QUEUE_SOURCE_CODEX_CONFIG: "/root/.codex/config.toml"
@@ -166,6 +166,7 @@ function remoteOpenCodeRunCommand(task: QueueTask, prompt: string): string {
`export MINIMAX_API_BASE=${ctx().shellQuote(ctx().config.minimaxApiBase)}`,
`export MINIMAX_MODEL=${ctx().shellQuote(ctx().config.minimaxModel)}`,
`export OPENCODE_CONFIG_CONTENT=${ctx().shellQuote(openCodeConfigContent())}`,
`export UNIDESK_SKILLS_PATH=${ctx().shellQuote(ctx().config.resolvedRunnerSkillsPath())}`,
...gitConfigEntries.map(([key, value], index) => `export GIT_CONFIG_KEY_$((\${GIT_CONFIG_COUNT:-0}+${index}))=${ctx().shellQuote(key)} GIT_CONFIG_VALUE_$((\${GIT_CONFIG_COUNT:-0}+${index}))=${ctx().shellQuote(value)}`),
`export GIT_CONFIG_COUNT=$((\${GIT_CONFIG_COUNT:-0}+${gitConfigEntries.length}))`,
].join("; ");
@@ -402,6 +402,7 @@ function readConfig(): RuntimeConfig {
executionProviderIds,
remoteCodexEnvKeys: envList("CODE_QUEUE_REMOTE_CODEX_ENV_KEYS", ["OPENAI_API_KEY", "CRS_OAI_KEY", "OPENAI_BASE_URL", "OPENAI_API_BASE", "DEEPSEEK_API_KEY", "DEEPSEEK_API_BASE", "DEEPSEEK_MODEL", "MINIMAX_API_KEY", "MINIMAX_API_BASE", "MINIMAX_MODEL", "GH_TOKEN", "GITHUB_TOKEN", "GH_HOST", "GITHUB_API_URL", "GH_REPO"]),
skillsPath: envString("UNIDESK_SKILLS_PATH", "/root/.agents/skills"),
runnerSkillsSourcePath: envString("CODE_QUEUE_RUNNER_SKILLS_SOURCE_PATH", "/home/ubuntu/.agents/skills"),
resolvedRunnerSkillsPath,
codexHome: envString("CODE_QUEUE_CODEX_HOME", "/var/lib/unidesk/code-queue/codex-home"),
opencodeXdgDir: envString("CODE_QUEUE_OPENCODE_XDG_DIR", resolve(dataDir, "opencode-xdg")),
@@ -8,7 +8,7 @@ export const codeQueueEnvironmentHintTitle = "# Code Queue 运行环境提示";
export const codeQueueEnvironmentHint = [
codeQueueEnvironmentHintTitle,
"如果当前 Code Queue Docker 容器缺少完成任务所需的环境、系统包或语言依赖,可以先在容器内临时安装以推进当前任务;同时必须把该依赖补到 `src/components/microservices/code-queue/Dockerfile`,让后续任务重建镜像后可直接使用。",
"任务可通过 `UNIDESK_SKILLS_PATH` 读取注入的宿主 skills;默认目标是 `/root/.agents/skills`如果该挂载暂缺但 approved source `/home/ubuntu/.agents/skills` 可读runner 会把 `UNIDESK_SKILLS_PATH` 解析到可用 source 并在 `/api/dev-ready` 的 `devReady.skills.resolution` 中报告需要 host rollout 的合同退化,不要读取或输出宿主 token/auth 配置。",
"任务可通过 `UNIDESK_SKILLS_PATH` 读取注入的宿主 skills;默认目标是 `/root/.agents/skills`如果该路径不可读或缺少必需 skillsrunner/preflight 会以 infra blocker 暴露 target projection 缺口;不要读取或输出宿主 token/auth 配置。",
].join("\n");
export function stripAutoReferenceHint(prompt: string): string {
@@ -8,7 +8,7 @@ import type { DevContainerCommandLog, DevContainerPlan, JsonValue, QueueTask, Ru
export interface ProviderRuntimeContext {
config: Pick<RuntimeConfig,
"codexHome" | "defaultWorkdir" | "devContainerDefaultProviderId" | "devContainerImage" | "devContainerMasterHost" | "devContainerWorkdir" | "executionProviderIds" | "mainProviderId" | "remoteCodexEnvKeys" | "remoteDefaultWorkdir" | "sourceCodexConfig" | "windowsNativeCodexBridgeDir" | "windowsNativeCodexCommand" | "windowsNativeCodexConnectHost" | "windowsNativeCodexDefaultWorkdir" | "windowsNativeCodexIdleTimeoutMs"
"codexHome" | "defaultWorkdir" | "devContainerDefaultProviderId" | "devContainerImage" | "devContainerMasterHost" | "devContainerWorkdir" | "executionProviderIds" | "mainProviderId" | "remoteCodexEnvKeys" | "remoteDefaultWorkdir" | "runnerSkillsSourcePath" | "skillsPath" | "sourceCodexConfig" | "windowsNativeCodexBridgeDir" | "windowsNativeCodexCommand" | "windowsNativeCodexConnectHost" | "windowsNativeCodexDefaultWorkdir" | "windowsNativeCodexIdleTimeoutMs"
>;
safePreview: (value: string, max?: number) => string;
}
@@ -432,12 +432,20 @@ REQUESTED_IMAGE=${shellQuote(plan.image)}
KEY_DIR=${shellQuote(plan.keyDir)}
WORKDIR=${shellQuote(plan.workdir)}
CONTAINER_WORKDIR=${shellQuote(plan.containerWorkdir)}
SKILLS_SOURCE=${shellQuote(ctx().config.runnerSkillsSourcePath)}
SKILLS_TARGET=${shellQuote(ctx().config.skillsPath)}
IMAGE="$REQUESTED_IMAGE"
SSH_DIR="\${UNIDESK_HOST_ROOT_SSH_DIR:-$HOME/.ssh}"
SSH_MOUNT_ARGS=()
if [ -d "$SSH_DIR" ]; then SSH_MOUNT_ARGS=(-v "$SSH_DIR":/root/.ssh:ro); fi
HOST_PATH_MOUNT_ARGS=()
if [ -d /mnt ]; then HOST_PATH_MOUNT_ARGS+=(-v /mnt:/mnt); fi
SKILLS_MOUNT_ARGS=()
if [ ! -d "$SKILLS_SOURCE" ]; then
echo "missing runner skills source $SKILLS_SOURCE; expected read-only projection to $SKILLS_TARGET" >&2
exit 1
fi
SKILLS_MOUNT_ARGS=(-v "$SKILLS_SOURCE":"$SKILLS_TARGET":ro)
if ! docker image inspect "$IMAGE" >/dev/null 2>&1; then
echo "missing dev container image $REQUESTED_IMAGE" >&2
exit 1
@@ -451,7 +459,7 @@ if [ ${forceRecreate ? "1" : "0"} -eq 0 ] && docker inspect "$CONTAINER" >/dev/n
STATE=$(docker inspect "$CONTAINER" --format '{{.State.Status}}' 2>/dev/null || true)
LABEL_WORKDIR=$(docker inspect "$CONTAINER" --format '{{ index .Config.Labels "unidesk.workdir" }}' 2>/dev/null || true)
if [ "$STATE" = "running" ] && [ "$LABEL_WORKDIR" = "$WORKDIR" ]; then
docker exec "$CONTAINER" bash -lc 'echo reuse_ready; command -v bash; test -d /run/unidesk-dev-proxy; test -d /var/lib/unidesk/code-queue/codex-home; test -d /var/lib/unidesk/code-queue/opencode-xdg' >/dev/null
docker exec "$CONTAINER" bash -lc 'echo reuse_ready; command -v bash; test "$UNIDESK_SKILLS_PATH" = "/root/.agents/skills"; test -r "$UNIDESK_SKILLS_PATH/docs-spec/SKILL.md"; test -r "$UNIDESK_SKILLS_PATH/cli-spec/SKILL.md"; test -r "$UNIDESK_SKILLS_PATH/frontend-design/SKILL.md"; if [ -r "$UNIDESK_SKILLS_PATH/playwright-cli/SKILL.md" ]; then :; else test -r "$UNIDESK_SKILLS_PATH/playwright/SKILL.md"; fi; test -d /run/unidesk-dev-proxy; test -d /var/lib/unidesk/code-queue/codex-home; test -d /var/lib/unidesk/code-queue/opencode-xdg' >/dev/null
echo "remote_container_reused container=$CONTAINER image=$(docker inspect "$CONTAINER" --format '{{.Config.Image}}') workdir=$WORKDIR"
exit 0
fi
@@ -473,6 +481,7 @@ cid=$(docker run -d \\
--env-file "$KEY_DIR/codex-env" \\
-e CODEX_HOME=${shellQuote(plan.remoteCodexHome)} \\
-e CODEX_INTERNAL_ORIGINATOR_OVERRIDE=unidesk_code_queue \\
-e UNIDESK_SKILLS_PATH="$SKILLS_TARGET" \\
-e XDG_DATA_HOME=${shellQuote(resolve(plan.remoteOpencodeXdgDir, "data"))} \\
-e XDG_CONFIG_HOME=${shellQuote(resolve(plan.remoteOpencodeXdgDir, "config"))} \\
-e XDG_CACHE_HOME=${shellQuote(resolve(plan.remoteOpencodeXdgDir, "cache"))} \\
@@ -484,14 +493,15 @@ cid=$(docker run -d \\
-v "$KEY_DIR/opencode-xdg":${shellQuote(plan.remoteOpencodeXdgDir)} \\
"\${SSH_MOUNT_ARGS[@]}" \\
"\${HOST_PATH_MOUNT_ARGS[@]}" \\
"\${SKILLS_MOUNT_ARGS[@]}" \\
-v "$WORKDIR":"$CONTAINER_WORKDIR" \\
-v "$WORKDIR":/root/unidesk \\
-w "$CONTAINER_WORKDIR" \\
"$IMAGE" \\
bash -lc 'mkdir -p /tmp/unidesk-tools; ln -sf /usr/local/bin/busybox /tmp/unidesk-tools/ip; ln -sf /usr/local/bin/busybox /tmp/unidesk-tools/ping; export PATH=/tmp/unidesk-tools:$PATH; echo ready; sleep infinity')
docker exec "$CONTAINER" bash -lc 'mkdir -p /tmp/unidesk-tools; ln -sf /usr/local/bin/busybox /tmp/unidesk-tools/ip; ln -sf /usr/local/bin/busybox /tmp/unidesk-tools/ping; export PATH=/tmp/unidesk-tools:$PATH; echo container=$(hostname); pwd; ip route show default; ls -ld /dev/net/tun /run/unidesk-dev-proxy/id_ed25519 /var/lib/unidesk/code-queue/codex-home /var/lib/unidesk/code-queue/opencode-xdg; command -v ssh; command -v ip; command -v ping'
docker exec "$CONTAINER" bash -lc 'mkdir -p /tmp/unidesk-tools; ln -sf /usr/local/bin/busybox /tmp/unidesk-tools/ip; ln -sf /usr/local/bin/busybox /tmp/unidesk-tools/ping; export PATH=/tmp/unidesk-tools:$PATH; echo container=$(hostname); pwd; ip route show default; test "$UNIDESK_SKILLS_PATH" = "/root/.agents/skills"; test -d "$UNIDESK_SKILLS_PATH"; test -r "$UNIDESK_SKILLS_PATH/docs-spec/SKILL.md"; test -r "$UNIDESK_SKILLS_PATH/cli-spec/SKILL.md"; test -r "$UNIDESK_SKILLS_PATH/frontend-design/SKILL.md"; if [ -r "$UNIDESK_SKILLS_PATH/playwright-cli/SKILL.md" ]; then :; else test -r "$UNIDESK_SKILLS_PATH/playwright/SKILL.md"; fi; ls -ld /dev/net/tun /run/unidesk-dev-proxy/id_ed25519 /var/lib/unidesk/code-queue/codex-home /var/lib/unidesk/code-queue/opencode-xdg "$UNIDESK_SKILLS_PATH"; command -v ssh; command -v ip; command -v ping'
docker exec "$CONTAINER" bash -lc 'command -v iptables; iptables --version'
echo "remote_container_ready container=$CONTAINER cid=$cid image=$IMAGE workdir=$WORKDIR"`;
echo "remote_container_ready container=$CONTAINER cid=$cid image=$IMAGE workdir=$WORKDIR skillsTarget=$SKILLS_TARGET"`;
}
function masterProxyPrepareScript(plan: DevContainerPlan): string {
@@ -1016,6 +1026,7 @@ function remoteAppServerCommand(task: QueueTask): string {
`cd ${shellQuote(task.cwd)}`,
`export CODEX_HOME=${shellQuote(plan.remoteCodexHome)}`,
"export CODEX_INTERNAL_ORIGINATOR_OVERRIDE=unidesk_code_queue",
`export UNIDESK_SKILLS_PATH=${shellQuote(ctx().config.skillsPath)}`,
gitConfigExports,
"exec codex app-server --listen stdio://",
].join("; ");
@@ -1093,3 +1104,8 @@ export {
throwIfCommandFailed,
windowsNativeAppServerCommand,
};
export const providerRuntimeForTest = {
remoteContainerStartScript,
remoteAppServerCommand,
};
@@ -124,6 +124,7 @@ export interface RuntimeConfig {
executionProviderIds: string[];
remoteCodexEnvKeys: string[];
skillsPath: string;
runnerSkillsSourcePath: string;
resolvedRunnerSkillsPath: () => string;
codexHome: string;
opencodeXdgDir: string;
@@ -82,6 +82,8 @@ spec:
value: "/root/.codex/config.toml"
- name: UNIDESK_SKILLS_PATH
value: "/root/.agents/skills"
- name: CODE_QUEUE_RUNNER_SKILLS_SOURCE_PATH
value: "/home/ubuntu/.agents/skills"
- name: CODE_QUEUE_DEFAULT_MODEL
value: "gpt-5.5"
- name: CODE_QUEUE_MODELS
@@ -328,6 +330,8 @@ spec:
value: "/root/.codex/config.toml"
- name: UNIDESK_SKILLS_PATH
value: "/root/.agents/skills"
- name: CODE_QUEUE_RUNNER_SKILLS_SOURCE_PATH
value: "/home/ubuntu/.agents/skills"
- name: CODE_QUEUE_DEFAULT_MODEL
value: "gpt-5.5"
- name: CODE_QUEUE_MODELS
@@ -1033,6 +1037,8 @@ spec:
value: "/root/.codex/config.toml"
- name: UNIDESK_SKILLS_PATH
value: "/root/.agents/skills"
- name: CODE_QUEUE_RUNNER_SKILLS_SOURCE_PATH
value: "/home/ubuntu/.agents/skills"
- name: CODE_QUEUE_DEFAULT_MODEL
value: "gpt-5.5"
- name: CODE_QUEUE_MODELS
@@ -331,6 +331,8 @@ spec:
value: never
- name: UNIDESK_SKILLS_PATH
value: /root/.agents/skills
- name: CODE_QUEUE_RUNNER_SKILLS_SOURCE_PATH
value: /home/ubuntu/.agents/skills
- name: CODE_QUEUE_EGRESS_PROXY_ENABLED
value: "true"
- name: CODE_QUEUE_EGRESS_PROXY_URL
@@ -542,6 +544,8 @@ spec:
value: "false"
- name: UNIDESK_SKILLS_PATH
value: /root/.agents/skills
- name: CODE_QUEUE_RUNNER_SKILLS_SOURCE_PATH
value: /home/ubuntu/.agents/skills
- name: CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED
value: "false"
- name: CODE_QUEUE_CODEX_SQLITE_LOG_EXPORT_ENABLED
@@ -694,6 +698,8 @@ spec:
value: "false"
- name: UNIDESK_SKILLS_PATH
value: /root/.agents/skills
- name: CODE_QUEUE_RUNNER_SKILLS_SOURCE_PATH
value: /home/ubuntu/.agents/skills
- name: CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED
value: "false"
- name: CODE_QUEUE_CODEX_SQLITE_LOG_EXPORT_ENABLED