feat: add D601 dev backend path

This commit is contained in:
Codex
2026-05-18 10:31:43 +00:00
parent 11b94f3a95
commit 37504a28e8
56 changed files with 10489 additions and 311 deletions
+19 -31
View File
@@ -20,7 +20,7 @@ export interface ContainerStatus {
ports: string;
}
const rebuildableServices = ["backend-core", "frontend", "provider-gateway", "todo-note", "code-queue-mgr", "project-manager", "baidu-netdisk", "oa-event-flow"] as const;
const rebuildableServices = ["backend-core", "frontend", "dev-frontend-proxy", "provider-gateway", "todo-note", "project-manager", "baidu-netdisk", "oa-event-flow", "code-queue-mgr"] as const;
export type RebuildableService = typeof rebuildableServices[number];
export function isRebuildableService(value: string | undefined): value is RebuildableService {
@@ -28,7 +28,6 @@ export function isRebuildableService(value: string | undefined): value is Rebuil
}
export function resolveComposeCommand(config: UniDeskConfig, envFile: string): string[] {
assertCanonicalServerRoot(config);
const composeFile = rootPath(config.docker.composeFile);
if (commandOk(["docker", "compose", "version"], repoRoot)) {
return ["docker", "compose", "--env-file", envFile, "-f", composeFile, "-p", config.docker.projectName];
@@ -54,21 +53,7 @@ function envValue(value: string): string {
return JSON.stringify(value);
}
function assertCanonicalServerRoot(config: UniDeskConfig): void {
const canonicalRoot = resolve(config.providerGateway.upgrade.hostProjectRoot);
const currentRoot = resolve(repoRoot);
if (currentRoot !== canonicalRoot) {
throw new Error([
"Main-server Docker Compose commands must run from the canonical UniDesk root.",
`canonicalRoot=${canonicalRoot}`,
`currentRoot=${currentRoot}`,
`Run from the canonical root: cd ${canonicalRoot} && bun scripts/cli.ts server <start|status|logs|rebuild|stop>`,
].join(" "));
}
}
export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean): ComposeRuntimeEnv {
assertCanonicalServerRoot(config);
const stateDir = rootPath(config.paths.stateDir);
mkdirSync(stateDir, { recursive: true });
const envFile = join(stateDir, "docker-compose.env");
@@ -107,6 +92,7 @@ export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean):
UNIDESK_PUBLIC_HOST: config.network.publicHost,
UNIDESK_CORE_PORT: String(config.network.core.port),
UNIDESK_FRONTEND_PORT: String(config.network.frontend.port),
UNIDESK_DEV_FRONTEND_PORT: String(config.network.devFrontend.port),
UNIDESK_DATABASE_PORT: String(config.network.database.port),
UNIDESK_DATABASE_BIND_HOST: runtimeSecretWithDefault("UNIDESK_DATABASE_BIND_HOST", restrictedHostBind, "127.0.0.1"),
UNIDESK_PROVIDER_INGRESS_PORT: String(config.network.providerIngress.port),
@@ -141,10 +127,6 @@ export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean):
UNIDESK_LOG_DAY: logDay,
UNIDESK_LOG_PREFIX: logPrefix,
UNIDESK_LOG_RETENTION_BYTES: runtimeSecret("UNIDESK_LOG_RETENTION_BYTES") || "1GiB",
UNIDESK_FRONTEND_DEPLOY_SERVICE_ID: runtimeSecret("UNIDESK_FRONTEND_DEPLOY_SERVICE_ID") || "frontend",
UNIDESK_FRONTEND_DEPLOY_REPO: runtimeSecret("UNIDESK_FRONTEND_DEPLOY_REPO"),
UNIDESK_FRONTEND_DEPLOY_COMMIT: runtimeSecret("UNIDESK_FRONTEND_DEPLOY_COMMIT"),
UNIDESK_FRONTEND_DEPLOY_REQUESTED_COMMIT: runtimeSecret("UNIDESK_FRONTEND_DEPLOY_REQUESTED_COMMIT"),
UNIDESK_HOST_ROOT_SSH_DIR: process.env.UNIDESK_HOST_ROOT_SSH_DIR || "/root/.ssh",
UNIDESK_HOST_SSH_KEY_DIR: config.sshForwarding.keyDir,
UNIDESK_HOST_SSH_HOST: config.sshForwarding.host,
@@ -159,13 +141,16 @@ export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean):
UNIDESK_TODO_NOTE_REMINDER_SCAN_INTERVAL_MS: runtimeSecret("UNIDESK_TODO_NOTE_REMINDER_SCAN_INTERVAL_MS") || "30000",
UNIDESK_TODO_NOTE_REMINDER_CLAUDEQQ_TIMEOUT_MS: runtimeSecret("UNIDESK_TODO_NOTE_REMINDER_CLAUDEQQ_TIMEOUT_MS") || "15000",
UNIDESK_TODO_NOTE_REMINDER_CLAUDEQQ_SEND_ATTEMPTS: runtimeSecret("UNIDESK_TODO_NOTE_REMINDER_CLAUDEQQ_SEND_ATTEMPTS") || "3",
UNIDESK_CODE_QUEUE_MGR_DATABASE_POOL_MAX: runtimeSecret("UNIDESK_CODE_QUEUE_MGR_DATABASE_POOL_MAX") || "2",
UNIDESK_CODE_QUEUE_TRACE_DATABASE_POOL_MAX: runtimeSecret("UNIDESK_CODE_QUEUE_TRACE_DATABASE_POOL_MAX") || "1",
UNIDESK_CODE_QUEUE_MINIMAX_API_KEY: runtimeSecret("UNIDESK_CODE_QUEUE_MINIMAX_API_KEY") || runtimeSecret("MINIMAX_API_KEY"),
UNIDESK_CODE_QUEUE_MINIMAX_MODEL: runtimeSecret("UNIDESK_CODE_QUEUE_MINIMAX_MODEL") || runtimeSecret("MINIMAX_MODEL") || "MiniMax-M2.7",
UNIDESK_CODE_QUEUE_MINIMAX_API_BASE: runtimeSecret("UNIDESK_CODE_QUEUE_MINIMAX_API_BASE") || runtimeSecret("MINIMAX_API_BASE") || "https://api.minimaxi.com/v1",
UNIDESK_CODE_QUEUE_MINIMAX_JUDGE_TIMEOUT_MS: runtimeSecretWithDefault("UNIDESK_CODE_QUEUE_MINIMAX_JUDGE_TIMEOUT_MS", "90000", "60000"),
UNIDESK_CODE_QUEUE_REMOTE_WORKDIR: runtimeSecret("UNIDESK_CODE_QUEUE_REMOTE_WORKDIR") || "/home/ubuntu",
UNIDESK_CODE_QUEUE_MAIN_PROVIDER_ID: runtimeSecret("UNIDESK_CODE_QUEUE_MAIN_PROVIDER_ID") || "D601",
UNIDESK_CODE_QUEUE_TRACE_DATABASE_URL: runtimeSecret("UNIDESK_CODE_QUEUE_TRACE_DATABASE_URL")
|| `postgres://${config.database.user}:${config.database.password}@database:${config.network.database.containerPort}/${config.database.name}`,
UNIDESK_CODE_QUEUE_MGR_DATABASE_POOL_MAX: runtimeSecret("UNIDESK_CODE_QUEUE_MGR_DATABASE_POOL_MAX") || "2",
UNIDESK_CODE_QUEUE_TRACE_DATABASE_POOL_MAX: runtimeSecret("UNIDESK_CODE_QUEUE_TRACE_DATABASE_POOL_MAX") || "1",
UNIDESK_CODE_QUEUE_EXECUTION_PROVIDER_IDS: runtimeSecret("UNIDESK_CODE_QUEUE_EXECUTION_PROVIDER_IDS") || "D601",
UNIDESK_CODE_QUEUE_DEV_CONTAINER_DEFAULT_PROVIDER_ID: runtimeSecret("UNIDESK_CODE_QUEUE_DEV_CONTAINER_DEFAULT_PROVIDER_ID") || "D601",
UNIDESK_CODE_QUEUE_DEV_CONTAINER_IMAGE: runtimeSecret("UNIDESK_CODE_QUEUE_DEV_CONTAINER_IMAGE"),
@@ -250,10 +235,11 @@ export function rebuildService(config: UniDeskConfig, service: RebuildableServic
const watchdogLog = rootPath(".state", "jobs", "compose-rebuild-watchdog.log");
const watchdogInnerScript = [
"set -euo pipefail",
"sleep 20",
`cid=$(${shellJoin(listServiceContainersCommand)} || true)`,
`if [ -z "$cid" ]; then echo "$(date -Is) compose_rebuild_watchdog_restore service=${service}" >> ${shellQuote(watchdogLog)}; ${shellJoin(restoreCommand)} >> ${shellQuote(watchdogLog)} 2>&1 || true; fi`,
].join("\n");
const watchdogScript = `set -euo pipefail; sleep 20; ${shellJoin(["flock", "-w", "300", lockPath, "bash", "-lc", watchdogInnerScript])} || true`;
const watchdogScript = `set -euo pipefail; ${shellJoin(["flock", "-w", "300", lockPath, "bash", "-lc", watchdogInnerScript])} || true`;
const validateScript = [
"ready=0",
"for attempt in $(seq 1 60); do",
@@ -308,6 +294,7 @@ export function rebuildService(config: UniDeskConfig, service: RebuildableServic
function fixedPorts(config: UniDeskConfig): Array<{ name: string; port: number; listening: boolean }> {
return [
{ name: "frontend", port: config.network.frontend.port, listening: isPortListening(config.network.frontend.port) },
{ name: "dev-frontend", port: config.network.devFrontend.port, listening: isPortListening(config.network.devFrontend.port) },
{ name: "provider-ingress", port: config.network.providerIngress.port, listening: isPortListening(config.network.providerIngress.port) },
];
}
@@ -356,9 +343,7 @@ function composeLockedScript(innerScript: string): string {
"set -euo pipefail",
`mkdir -p ${shellQuote(rootPath(".state", "locks"))}`,
`echo ${shellJoin(["compose_lock_wait", lockPath])}`,
// Prevent background helpers spawned by the locked script from inheriting
// the compose lock fd and blocking later server rebuild jobs.
shellJoin(["flock", "-o", lockPath, "bash", "-lc", innerScript]),
shellJoin(["flock", lockPath, "bash", "-lc", innerScript]),
].join("; ");
}
@@ -409,8 +394,8 @@ async function probe(url: string): Promise<unknown> {
}
}
function dockerExecJson(container: string, code: string): unknown {
const result = runCommand(["docker", "exec", container, "bun", "-e", code], repoRoot);
function dockerExecJson(container: string, path: string): unknown {
const result = runCommand(["docker", "exec", container, "backend-core", "--fetch-json", `http://127.0.0.1:8080${path}`], repoRoot);
if (result.exitCode !== 0) {
return { ok: false, exitCode: result.exitCode, stdout: result.stdout.slice(-1200), stderr: result.stderr.slice(-1200) };
}
@@ -433,8 +418,8 @@ export async function stackStatus(config: UniDeskConfig): Promise<unknown> {
const databaseBindHost = runtimeValue("UNIDESK_DATABASE_BIND_HOST") || "127.0.0.1";
const oaEventFlowBindHost = runtimeValue("UNIDESK_OA_EVENT_FLOW_BIND_HOST") || "127.0.0.1";
const oaEventFlowPort = Number(runtimeValue("UNIDESK_OA_EVENT_FLOW_PORT") || "4255");
const coreHealth = dockerExecJson("unidesk-backend-core", "fetch('http://127.0.0.1:8080/health').then(r=>r.json()).then(j=>console.log(JSON.stringify({ok:true,status:200,body:j}))).catch(e=>{console.log(JSON.stringify({ok:false,error:String(e)}));process.exit(1)})");
const overview = dockerExecJson("unidesk-backend-core", "fetch('http://127.0.0.1:8080/api/overview').then(r=>r.json()).then(j=>console.log(JSON.stringify({ok:true,status:200,body:j}))).catch(e=>{console.log(JSON.stringify({ok:false,error:String(e)}));process.exit(1)})");
const coreHealth = dockerExecJson("unidesk-backend-core", "/health");
const overview = dockerExecJson("unidesk-backend-core", "/api/overview");
return {
runtimeEnv,
swap: swapStatus(),
@@ -462,6 +447,7 @@ export async function stackStatus(config: UniDeskConfig): Promise<unknown> {
],
internalPorts: [
{ name: "backend-core", containerPort: config.network.core.containerPort, hostPort: null },
{ name: "dev-frontend-proxy", containerPort: config.network.devFrontend.containerPort, hostPort: config.network.devFrontend.port },
{ name: "database", containerPort: config.network.database.containerPort, hostPort: null },
{ name: "project-manager", containerPort: 4233, hostPort: null },
{ name: "baidu-netdisk", containerPort: 4244, hostPort: null },
@@ -471,12 +457,14 @@ export async function stackStatus(config: UniDeskConfig): Promise<unknown> {
health: {
core: coreHealth,
frontend: await probe(`http://127.0.0.1:${config.network.frontend.port}/health`),
devFrontend: await probe(`http://127.0.0.1:${config.network.devFrontend.port}/health`),
providerIngress: await probe(`http://127.0.0.1:${config.network.providerIngress.port}/health`),
database: dockerExec(config, "unidesk-database", ["pg_isready", "-U", config.database.user, "-d", config.database.name]),
overview,
},
urls: {
frontend: `http://${config.network.publicHost}:${config.network.frontend.port}`,
devFrontend: `http://${config.network.publicHost}:${config.network.devFrontend.port}`,
providerIngress: `ws://${config.network.publicHost}:${config.network.providerIngress.port}/ws/provider`,
internalCore: `http://backend-core:${config.network.core.containerPort}`,
internalDatabase: `postgres://${config.database.user}:***@database:${config.network.database.containerPort}/${config.database.name}`,
@@ -507,7 +495,7 @@ export function stackLogs(config: UniDeskConfig, tailBytes: number): unknown {
const truncated = sizeBytes > tailBytes;
return { path, name: basename(path), sizeBytes, tailBytes, truncated, tail: tailFile(path, tailBytes) };
});
const containerNames = ["unidesk-database", "unidesk-backend-core", "unidesk-frontend", "unidesk-provider-gateway-main", "todo-note-backend", "project-manager-backend", "baidu-netdisk-backend", "oa-event-flow-backend"];
const containerNames = ["unidesk-database", "unidesk-backend-core", "unidesk-frontend", "unidesk-dev-frontend-proxy", "unidesk-provider-gateway-main", "todo-note-backend", "project-manager-backend", "baidu-netdisk-backend", "oa-event-flow-backend"];
const docker = containerNames.map((name) => {
const result = runCommand(["docker", "logs", "--tail", "40", name], repoRoot);
return {