// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. scripts module for scripts/src/deploy.ts.

// Moved mechanically from scripts/src/deploy.ts:1519-2218 for #903.

import { createHash } from "node:crypto";
import { existsSync, mkdirSync, readFileSync } from "node:fs";
import { join, resolve } from "node:path";
import { pathToFileURL } from "node:url";
import { runCommand } from "../command";
import { type UniDeskConfig, type UniDeskMicroserviceConfig, repoRoot, rootPath } from "../config";
import { ensureGithubSshIdentityForProvider } from "../deploy-ssh-identity";
import { baiduNetdiskRuntimeSecretRequirements, runtimeSecretContractFromEnvText, type RuntimeSecretContract, runArtifactRegistryCommand } from "../artifact-registry";
import { startJob } from "../jobs";
import { coreInternalFetch } from "../microservices";
import { codeQueueSourceImportPreflight, codeQueueSourceSubdir } from "../code-queue-source-guard";
import { k3sGuardShellLines, defaultNativeKubeconfig } from "../k3s-target-guard";
import { composeRuntimeEnvValue } from "../runtime-env";
import {
  compareDeployJsonExecutorMirrors,
  deployJsonCommitImage,
  deployJsonDriftResult,
  deployJsonSourceOfTruth,
  encodeDeployJsonServiceContract,
  hasDeployJsonExecutorContract,
  k3sManifestExecutorMirror,
  parseDeployJsonServiceContract,
  type DeployJsonExecutorMirror,
  type DeployJsonServiceContract,
} from "../deploy-json-contract";

import type { DeployManifestService } from "./types";
import { k3sTargetGuardScript, isUnideskRepo, providerSourceRepoUrl, safeId, shellQuote } from "./options";
import { buildBaseImageFallbacks, buildCachePrelude, buildImageTag, devK3sPrepullImages, directBuildContextOverride, directComposeEnvFile, directComposeFile, directDockerfileOverride, k8sManifestPath, sourceBuildContext, sourceDockerfilePath, sourceFallbackWorktree, sourceProxyPrelude, sourceWorkDir, targetIsMain, targetRepoDir, targetWorkDir } from "./paths";
import { isDevK3sDeployService, isDirectComposeDeployMode } from "./service-plan";
import { k8sKubeconfig, nativeK3sCtrAddress, nativeK3sImage, nativeK3sInstallVersion, providerGatewayWsEgressProxyUrl } from "./types";

export function syncDevFrontendAuthScript(config: UniDeskConfig): string {
  const sshClientToken = composeRuntimeEnvValue("UNIDESK_SSH_CLIENT_TOKEN");
  if (sshClientToken === null) throw new Error(`UNIDESK_SSH_CLIENT_TOKEN must be present in ${rootPath(".state", "docker-compose.env")} before deploying dev frontend`);
  const sshClientRouteAllowlist = composeRuntimeEnvValue("UNIDESK_SSH_CLIENT_ROUTE_ALLOWLIST") ?? "G14,G14:*,D601,D601:*";
  const data = {
    AUTH_USERNAME: Buffer.from(config.auth.username, "utf8").toString("base64"),
    AUTH_PASSWORD: Buffer.from(config.auth.password, "utf8").toString("base64"),
    SESSION_SECRET: Buffer.from(config.auth.sessionSecret, "utf8").toString("base64"),
    UNIDESK_SSH_CLIENT_TOKEN: Buffer.from(sshClientToken, "utf8").toString("base64"),
  };
  const runtimeConfig = {
    SESSION_TTL_SECONDS: String(config.auth.sessionTtlSeconds),
    UNIDESK_SSH_CLIENT_ROUTE_ALLOWLIST: sshClientRouteAllowlist,
  };
  return [
    "set -euo pipefail",
    k3sTargetGuardScript(),
    `secret_patch=${shellQuote(JSON.stringify({ data }))}`,
    `config_patch=${shellQuote(JSON.stringify({ data: runtimeConfig }))}`,
    `kubectl -n unidesk-dev patch secret unidesk-dev-runtime-secrets --type merge -p "$secret_patch"`,
    `kubectl -n unidesk-dev patch configmap unidesk-dev-runtime-config --type merge -p "$config_patch"`,
    "echo dev_frontend_auth_synced=ok",
  ].join("\n");
}

export function prepareSourceScript(service: UniDeskMicroserviceConfig, desired: DeployManifestService, exportDir: string): string {
  if (targetIsMain(service) && isUnideskRepo(desired.repo)) {
    return [
      "set -euo pipefail",
      `repo=${shellQuote(repoRoot)}`,
      `commit=${shellQuote(desired.commitId)}`,
      `export_dir=${shellQuote(exportDir)}`,
      "mkdir -p \"$(dirname \"$export_dir\")\"",
      "git -C \"$repo\" fetch --no-tags origin \"$commit\" || git -C \"$repo\" fetch --no-tags --prune origin '+refs/heads/*:refs/remotes/origin/*' '+refs/tags/*:refs/tags/*'",
      "resolved=$(git -C \"$repo\" rev-parse --verify \"$commit^{commit}\")",
      "rm -rf \"$export_dir\"",
      "mkdir -p \"$export_dir\"",
      "git -C \"$repo\" archive --format=tar \"$resolved\" | tar -xf - -C \"$export_dir\"",
      "printf 'resolved_commit=%s\\nexport_dir=%s\\nsource_repo=%s\\n' \"$resolved\" \"$export_dir\" \"$repo\"",
    ].join("\n");
  }
  const fallbackWorktree = sourceFallbackWorktree(service);
  return [
    "set -euo pipefail",
    sourceProxyPrelude(service),
    `repo=${shellQuote(targetRepoDir(service))}`,
    `repo_url=${shellQuote(providerSourceRepoUrl(desired.repo))}`,
    `commit=${shellQuote(desired.commitId)}`,
    `export_dir=${shellQuote(exportDir)}`,
    `fallback_worktree=${shellQuote(fallbackWorktree)}`,
    "mkdir -p \"$(dirname \"$repo\")\" \"$(dirname \"$export_dir\")\"",
    "source_repo=\"\"",
    "source_mode=\"remote-fetch\"",
    "source_log=$(mktemp /tmp/unidesk-deploy-source.XXXXXX.log)",
    "if { [ -d \"$repo/.git\" ] || { rm -rf \"$repo\" && git clone --no-checkout \"$repo_url\" \"$repo\"; }; } >\"$source_log\" 2>&1; then",
    "  cd \"$repo\"",
    "  if { git remote set-url origin \"$repo_url\" && { git fetch --no-tags origin \"$commit\" || git fetch --no-tags --prune origin '+refs/heads/*:refs/remotes/origin/*' '+refs/tags/*:refs/tags/*'; } && git rev-parse --verify \"$commit^{commit}\" > /tmp/unidesk-deploy-resolved-commit; } >>\"$source_log\" 2>&1; then",
    "    resolved=$(cat /tmp/unidesk-deploy-resolved-commit)",
    "    source_repo=\"$repo\"",
    "  fi",
    "fi",
    "if [ -z \"$source_repo\" ]; then",
    "  echo target_source_remote_prepare=failed",
    "  tail -80 \"$source_log\" || true",
    "  if [ -n \"$fallback_worktree\" ] && git -C \"$fallback_worktree\" rev-parse --is-inside-work-tree >/dev/null 2>&1 && git -C \"$fallback_worktree\" rev-parse --verify \"$commit^{commit}\" > /tmp/unidesk-deploy-resolved-commit 2>/dev/null; then",
    "    resolved=$(cat /tmp/unidesk-deploy-resolved-commit)",
    "    source_repo=$(git -C \"$fallback_worktree\" rev-parse --show-toplevel)",
    "    source_mode=\"provider-worktree\"",
    "    printf 'target_source_fallback_worktree=%s\\n' \"$fallback_worktree\"",
    "  else",
    "    cat \"$source_log\" >&2 || true",
    "    exit 128",
    "  fi",
    "fi",
    "rm -rf \"$export_dir\"",
    "mkdir -p \"$export_dir\"",
    "git -C \"$source_repo\" archive --format=tar \"$resolved\" | tar -xf - -C \"$export_dir\"",
    "rm -f \"$source_log\" /tmp/unidesk-deploy-resolved-commit",
    "printf 'resolved_commit=%s\\nexport_dir=%s\\nsource_repo=%s\\nsource_mode=%s\\n' \"$resolved\" \"$export_dir\" \"$source_repo\" \"$source_mode\"",
  ].filter((line) => line.length > 0).join("\n");
}

export function syncSourceScript(service: UniDeskMicroserviceConfig, exportDir: string): string {
  const workDir = sourceWorkDir(service);
  const buildContext = sourceBuildContext(service);
  const dockerfilePath = sourceDockerfilePath(service);
  const overlayCommands = service.id === "claudeqq" ? claudeqqDeployAssetOverlayCommands() : [];
  return [
    "set -euo pipefail",
    `export_dir=${shellQuote(exportDir)}`,
    `work_dir=${shellQuote(workDir)}`,
    `build_context=${shellQuote(buildContext)}`,
    `dockerfile_path=${shellQuote(dockerfilePath)}`,
    "mkdir -p \"$work_dir\"",
    [
      "rsync -a --delete",
      "--exclude '.git/'",
      "--exclude '.state/'",
      "--exclude 'logs/'",
      "--exclude '**/node_modules/'",
      "--exclude '**/dist/'",
      "\"$export_dir/\"",
      "\"$work_dir/\"",
    ].join(" "),
    ...overlayCommands,
    "test -f \"$build_context/$dockerfile_path\"",
    "printf 'synced deploy worktree to %s\\nbuild_context=%s\\n' \"$work_dir\" \"$build_context\"",
  ].join("\n");
}

export function codeQueueSourcePreflightScript(service: UniDeskMicroserviceConfig): string {
  if (service.id !== "code-queue") return "";
  const workDir = sourceWorkDir(service);
  return [
    "set -euo pipefail",
    `guard_root=${shellQuote(workDir)}`,
    "if [ -f \"$guard_root/scripts/code-queue-source-guard.ts\" ] && command -v bun >/dev/null 2>&1; then",
    "  bun \"$guard_root/scripts/code-queue-source-guard.ts\" --root \"$guard_root\"",
    "else",
    "  python3 - \"$guard_root\" <<'PY'",
    "import json",
    "import os",
    "import re",
    "import sys",
    "",
    `SOURCE_SUBDIR = ${JSON.stringify(codeQueueSourceSubdir)}`,
    "EXTENSIONS = ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs', '.json']",
    "",
    "def rel(root, path):",
    "    return os.path.relpath(path, root).replace(os.sep, '/')",
    "",
    "def strip_comments(text):",
    "    text = re.sub(r'/\\*[\\s\\S]*?\\*/', '', text)",
    "    return re.sub(r'(^|[^:])//.*$', r'\\1', text, flags=re.MULTILINE)",
    "",
    "def specifiers(text):",
    "    clean = strip_comments(text)",
    "    values = set()",
    "    patterns = [",
    "        re.compile(r'\\b(?:import|export)\\s+(?:type\\s+)?(?:[\\s\\S]*?\\s+from\\s+)?[\"\\'](\\.{1,2}/[^\"\\']+)[\"\\']'),",
    "        re.compile(r'\\bimport\\s*\\(\\s*[\"\\'](\\.{1,2}/[^\"\\']+)[\"\\']\\s*\\)'),",
    "    ]",
    "    for pattern in patterns:",
    "        values.update(match.group(1) for match in pattern.finditer(clean))",
    "    return sorted(values)",
    "",
    "def candidates(importer, specifier):",
    "    base = os.path.abspath(os.path.join(os.path.dirname(importer), specifier))",
    "    if any(base.endswith(extension) for extension in EXTENSIONS):",
    "        return [base]",
    "    return [base + extension for extension in EXTENSIONS] + [os.path.join(base, 'index' + extension) for extension in EXTENSIONS]",
    "",
    "root = os.path.abspath(sys.argv[1])",
    "source_root = os.path.join(root, SOURCE_SUBDIR)",
    "if not os.path.isdir(source_root):",
    "    print(json.dumps({'ok': False, 'guard': 'code-queue-hostpath-source-imports', 'root': root, 'sourceRoot': source_root, 'checkedFiles': 0, 'checkedImports': 0, 'missing': [], 'degradedReason': 'source-root-missing', 'message': 'Code Queue source root is missing: ' + SOURCE_SUBDIR}, ensure_ascii=False))",
    "    raise SystemExit(1)",
    "files = []",
    "for current_root, dirs, names in os.walk(source_root):",
    "    dirs[:] = [name for name in dirs if name not in ('node_modules', 'dist', '.git', '.state')]",
    "    for name in names:",
    "        if name.endswith('.ts') or name.endswith('.tsx'):",
    "            files.append(os.path.join(current_root, name))",
    "files.sort()",
    "missing = []",
    "checked_imports = 0",
    "for path in files:",
    "    with open(path, encoding='utf-8') as handle:",
    "        text = handle.read()",
    "    for specifier in specifiers(text):",
    "        checked_imports += 1",
    "        expected = candidates(path, specifier)",
    "        if any(os.path.isfile(candidate) for candidate in expected):",
    "            continue",
    "        missing.append({'importer': rel(root, path), 'specifier': specifier, 'expected': [rel(root, candidate) for candidate in expected]})",
    "result = {'ok': not missing, 'guard': 'code-queue-hostpath-source-imports', 'root': root, 'sourceRoot': source_root, 'checkedFiles': len(files), 'checkedImports': checked_imports, 'missing': missing, 'degradedReason': 'none' if not missing else 'missing-relative-import-target', 'message': 'Code Queue hostPath source import preflight passed (%d files, %d relative imports).' % (len(files), checked_imports) if not missing else 'Code Queue hostPath source import preflight failed: %d relative import target(s) are missing.' % len(missing)}",
    "print(json.dumps(result, ensure_ascii=False))",
    "raise SystemExit(0 if result['ok'] else 1)",
    "PY",
    "fi",
  ].join("\n");
}

export function claudeqqDeployAssetOverlayCommands(): string[] {
  const assets = [
    {
      relativePath: "$build_context/$dockerfile_path",
      sourcePath: rootPath("src/components/microservices/claudeqq/Dockerfile"),
      label: "Dockerfile",
    },
    {
      relativePath: "$build_context/unidesk-adapter.cjs",
      sourcePath: rootPath("src/components/microservices/claudeqq/adapter.js"),
      label: "unidesk-adapter.cjs",
    },
  ];
  return assets.flatMap((asset) => {
    if (!existsSync(asset.sourcePath)) throw new Error(`claudeqq deploy asset missing: ${asset.sourcePath}`);
    const encoded = Buffer.from(readFileSync(asset.sourcePath, "utf8"), "utf8").toString("base64");
    return [
      `mkdir -p "$(dirname "${asset.relativePath}")"`,
      `printf %s ${shellQuote(encoded)} | base64 -d > "${asset.relativePath}"`,
      `printf 'synced_claudeqq_deploy_asset=%s\\n' ${shellQuote(asset.label)}`,
    ];
  });
}

export function syncK8sControlManifestsScript(service: UniDeskMicroserviceConfig): string {
  if (isDevK3sDeployService(service)) {
    const manifest = k8sManifestPath(service);
    return [
      "set -euo pipefail",
      `target_root=${shellQuote(targetWorkDir(service))}`,
      `relative_path=${shellQuote(manifest)}`,
      "target_file=\"$target_root/$relative_path\"",
      "test -f \"$target_file\"",
      "printf 'target_k3s_control_manifest=%s\\n' \"$target_file\"",
    ].join("\n");
  }
  if (service.deployment.mode !== "k3sctl-managed" || isUnideskRepo(service.repository.url)) return "";
  const manifests = service.repository.composeFile.endsWith(".k8s.yaml")
    ? [service.repository.composeFile]
    : [service.repository.composeFile, k8sManifestPath(service)];
  const commands = [
    "set -euo pipefail",
    `target_root=${shellQuote(targetWorkDir(service))}`,
    "mkdir -p \"$target_root\"",
  ];
  for (const relativePath of manifests) {
    const absolutePath = rootPath(relativePath);
    if (!existsSync(absolutePath)) throw new Error(`${service.id} k3s control manifest missing: ${relativePath}`);
    const encoded = Buffer.from(readFileSync(absolutePath, "utf8"), "utf8").toString("base64");
    commands.push(
      `relative_path=${shellQuote(relativePath)}`,
      `target_file="$target_root/$relative_path"`,
      "mkdir -p \"$(dirname \"$target_file\")\"",
      `printf %s ${shellQuote(encoded)} | base64 -d > "$target_file"`,
      "printf 'synced_k3s_control_manifest=%s\\n' \"$target_file\"",
    );
  }
  return commands.join("\n");
}

export function buildImageScript(service: UniDeskMicroserviceConfig, desired: DeployManifestService, resolvedCommit: string): string {
  const image = buildImageTag(service);
  const buildContext = sourceBuildContext(service);
  const dockerfilePath = sourceDockerfilePath(service);
  const dockerfile = `${buildContext}/${dockerfilePath}`;
  const labelArgs = [
    "--label", `unidesk.ai/service-id=${service.id}`,
    "--label", `unidesk.ai/source-repo=${desired.repo}`,
    "--label", `unidesk.ai/source-commit=${resolvedCommit}`,
    "--label", `unidesk.ai/dockerfile=${dockerfilePath}`,
  ];
  const commonArgs = [
    "--progress=plain",
    ...labelArgs,
    "-t", image,
    "-f", dockerfile,
    buildContext,
  ];
  const proxyBuildArgs = targetIsMain(service)
    ? []
    : [
      "--network", "host",
      "--build-arg", `HTTP_PROXY=${providerGatewayWsEgressProxyUrl}`,
      "--build-arg", `HTTPS_PROXY=${providerGatewayWsEgressProxyUrl}`,
      "--build-arg", `ALL_PROXY=${providerGatewayWsEgressProxyUrl}`,
      "--build-arg", "NO_PROXY=localhost,127.0.0.1,::1,host.docker.internal",
    ];
  return [
    "set -euo pipefail",
    sourceProxyPrelude(service),
    `image=${shellQuote(image)}`,
    `dockerfile=${shellQuote(dockerfile)}`,
    "if ! docker buildx version >/dev/null 2>&1; then",
    "  if ! docker buildx inspect default >/dev/null 2>&1; then echo target_build_builder=missing >&2; exit 1; fi",
    "fi",
    ...devK3sPrepullImages(service).flatMap((imageName) => [
      `if ! docker image inspect ${shellQuote(imageName)} >/dev/null 2>&1; then`,
      `  docker pull ${shellQuote(imageName)}`,
      "fi",
    ]),
    "builder_args=()",
    "if docker buildx inspect --builder default >/dev/null 2>&1; then builder_args=(--builder default); echo target_build_builder=default; else echo target_build_builder=implicit; fi",
    "docker buildx inspect \"${builder_args[@]}\" --bootstrap || true",
    "echo target_build_builder_cleanup=not-required",
    ...buildCachePrelude("$dockerfile", buildBaseImageFallbacks(service)),
    `docker buildx build "\${builder_args[@]}" --load "\${cache_args[@]}" "\${base_args[@]}" ${[...proxyBuildArgs, ...commonArgs].map(shellQuote).join(" ")}`,
    "docker image inspect \"$image\" --format 'image_id={{.Id}} labels={{json .Config.Labels}}'",
  ].filter((line) => line.length > 0).join("\n");
}

export function patchDevK3sManifestScript(service: UniDeskMicroserviceConfig, desired: DeployManifestService, resolvedCommit: string): string {
  const manifest = `${targetWorkDir(service)}/${k8sManifestPath(service)}`;
  const image = buildImageTag(service);
  const serviceId = service.id;
  const deploymentName = service.repository.composeService;
  return [
    "set -euo pipefail",
    `manifest=${shellQuote(manifest)}`,
    `service_id=${shellQuote(serviceId)}`,
    `deployment_name=${shellQuote(deploymentName)}`,
    `image=${shellQuote(image)}`,
    `repo=${shellQuote(desired.repo)}`,
    `commit=${shellQuote(resolvedCommit)}`,
    `requested_commit=${shellQuote(desired.commitId)}`,
    `python3 - "$manifest" "$service_id" "$deployment_name" "$image" "$repo" "$commit" "$requested_commit" <<'PY'`,
    "import re",
    "import sys",
    "path, service_id, deployment_name, image, repo, commit, requested_commit = sys.argv[1:]",
    "text = open(path, encoding='utf-8').read()",
    "segments = re.split(r'(?m)^---\\s*$', text)",
    "kept = []",
    "for segment in segments:",
    "    if not segment.strip():",
    "        continue",
    "    haystack = '\\n' + segment + '\\n'",
    "    if f'unidesk.ai/deploy-service-id: {service_id}' in segment or f'\\n  name: {deployment_name}\\n' in haystack:",
    "        kept.append(segment)",
    "if not kept:",
    "    raise SystemExit(f'dev service {service_id}/{deployment_name} not found in {path}')",
    "patched = []",
    "for segment in kept:",
    "    segment = segment.replace('unidesk.ai/image-source: deploy-env-commit', 'unidesk.ai/image-source: deploy-env-commit')",
    "    segment = re.sub(r'image: unidesk-[^\\n]+:dev-placeholder', f'image: {image}', segment)",
    "    segment = re.sub(r'value: replace-with-deploy-env-commit', f'value: {commit}', segment)",
    "    segment = segment.replace('value: https://github.com/pikasTech/unidesk', f'value: {repo}')",
    "    patched.append(segment.strip() + '\\n')",
    "out = '---\\n'.join(patched)",
    "open(path, 'w', encoding='utf-8').write(out)",
    "print(f'dev_k3s_manifest_patched={path}')",
    "print(f'dev_k3s_manifest_service={service_id}')",
    "print(f'dev_k3s_manifest_deployment={deployment_name}')",
    "print(f'dev_k3s_manifest_image={image}')",
    "print(f'dev_k3s_manifest_commit={commit}')",
    "PY",
  ].join("\n");
}

export function directComposeResolveScript(service: UniDeskMicroserviceConfig): string {
  const projectHint = targetIsMain(service) ? "unidesk" : "";
  return [
    `work_dir=${shellQuote(targetWorkDir(service))}`,
    `compose_file=${shellQuote(directComposeFile(service))}`,
    `compose_env_file=${shellQuote(directComposeEnvFile(service))}`,
    `compose_service=${shellQuote(service.repository.composeService)}`,
    `container=${shellQuote(service.repository.containerName)}`,
    `project_hint=${shellQuote(projectHint)}`,
    `build_context_override=${shellQuote(directBuildContextOverride(service))}`,
    `build_dockerfile_override=${shellQuote(directDockerfileOverride(service))}`,
    "compose_env_args=()",
    "if [ -n \"$compose_env_file\" ]; then compose_env_args=(--env-file \"$compose_env_file\"); fi",
    "running_project=$(docker inspect -f '{{ index .Config.Labels \"com.docker.compose.project\" }}' \"$container\" 2>/dev/null || true)",
    "running_service=$(docker inspect -f '{{ index .Config.Labels \"com.docker.compose.service\" }}' \"$container\" 2>/dev/null || true)",
    "running_image=$(docker inspect -f '{{.Config.Image}}' \"$container\" 2>/dev/null || true)",
    "if [ \"$running_project\" = '<no value>' ]; then running_project=''; fi",
    "if [ \"$running_service\" = '<no value>' ]; then running_service=''; fi",
    "project=\"$running_project\"",
    "if [ -z \"$project\" ]; then project=\"$project_hint\"; fi",
    "if [ -z \"$project\" ]; then project=$(basename \"$work_dir\"); fi",
    "config_json=$(docker compose \"${compose_env_args[@]}\" -f \"$compose_file\" -p \"$project\" config --format json)",
    "compose_image=$(printf '%s' \"$config_json\" | python3 -c 'import json,sys; svc=sys.argv[1]; d=json.load(sys.stdin); print(((d.get(\"services\") or {}).get(svc) or {}).get(\"image\") or \"\")' \"$compose_service\")",
    "image=\"$compose_image\"",
    "if [ -z \"$image\" ]; then image=\"$running_image\"; fi",
    "if [ -z \"$image\" ]; then image=\"${project}-${compose_service}\"; fi",
    "build_context=$(printf '%s' \"$config_json\" | python3 -c 'import json,sys; svc=sys.argv[1]; b=(((json.load(sys.stdin).get(\"services\") or {}).get(svc) or {}).get(\"build\") or {}); print((b if isinstance(b,str) else b.get(\"context\")) or \".\")' \"$compose_service\")",
    "build_dockerfile=$(printf '%s' \"$config_json\" | python3 -c 'import json,sys; svc=sys.argv[1]; b=(((json.load(sys.stdin).get(\"services\") or {}).get(svc) or {}).get(\"build\") or {}); print(\"Dockerfile\" if isinstance(b,str) else (b.get(\"dockerfile\") or \"Dockerfile\"))' \"$compose_service\")",
    "build_target=$(printf '%s' \"$config_json\" | python3 -c 'import json,sys; svc=sys.argv[1]; b=(((json.load(sys.stdin).get(\"services\") or {}).get(svc) or {}).get(\"build\") or {}); print(\"\" if isinstance(b,str) else (b.get(\"target\") or \"\"))' \"$compose_service\")",
    "build_args_file=$(mktemp /tmp/unidesk-compose-build-args.XXXXXX)",
    "printf '%s' \"$config_json\" | python3 -c 'import json,sys; svc=sys.argv[1]; b=(((json.load(sys.stdin).get(\"services\") or {}).get(svc) or {}).get(\"build\") or {}); args={} if isinstance(b,str) else (b.get(\"args\") or {}); items=args.items() if isinstance(args,dict) else [(str(x).split(\"=\",1)[0], str(x).split(\"=\",1)[1] if \"=\" in str(x) else \"\") for x in args]; [print(str(k)+\"=\"+(\"\" if v is None else str(v))) for k,v in items]' \"$compose_service\" > \"$build_args_file\"",
    "if [ -n \"$build_context_override\" ]; then build_context=\"$build_context_override\"; fi",
    "if [ -n \"$build_dockerfile_override\" ]; then build_dockerfile=\"$build_dockerfile_override\"; fi",
    "case \"$build_context\" in /*) ;; *) build_context=$(cd \"$(dirname \"$compose_file\")\" && cd \"$build_context\" && pwd) ;; esac",
    "case \"$build_dockerfile\" in /*) dockerfile_abs=\"$build_dockerfile\" ;; *) dockerfile_abs=\"$build_context/$build_dockerfile\" ;; esac",
    "test -f \"$dockerfile_abs\"",
    "printf 'compose_project=%s\\ncompose_service=%s\\ncompose_image=%s\\nbuild_context=%s\\nbuild_dockerfile=%s\\nbuild_target=%s\\n' \"$project\" \"$compose_service\" \"$image\" \"$build_context\" \"$dockerfile_abs\" \"$build_target\"",
  ].join("\n");
}

export function buildDirectImageScript(service: UniDeskMicroserviceConfig, desired: DeployManifestService, resolvedCommit: string): string {
  const proxyBuildArgs = targetIsMain(service)
    ? []
    : [
      "--network", "host",
      "--build-arg", `HTTP_PROXY=${providerGatewayWsEgressProxyUrl}`,
      "--build-arg", `HTTPS_PROXY=${providerGatewayWsEgressProxyUrl}`,
      "--build-arg", `ALL_PROXY=${providerGatewayWsEgressProxyUrl}`,
      "--build-arg", "NO_PROXY=localhost,127.0.0.1,::1,host.docker.internal",
    ];
  const labelArgs = [
    "--label", `unidesk.ai/service-id=${service.id}`,
    "--label", `unidesk.ai/source-repo=${desired.repo}`,
    "--label", `unidesk.ai/source-commit=${resolvedCommit}`,
    "--label", `unidesk.ai/dockerfile=${service.repository.dockerfile}`,
  ];
  return [
    "set -euo pipefail",
    sourceProxyPrelude(service),
    directComposeResolveScript(service),
    "builder_args=()",
    "if docker buildx inspect --builder default >/dev/null 2>&1; then builder_args=(--builder default); echo target_build_builder=default; else echo target_build_builder=implicit; fi",
    "docker buildx inspect \"${builder_args[@]}\" --bootstrap || true",
    ...buildCachePrelude("$dockerfile_abs", buildBaseImageFallbacks(service)),
    "compose_build_args=()",
    "while IFS= read -r item; do [ -n \"$item\" ] && compose_build_args+=(--build-arg \"$item\"); done < \"$build_args_file\"",
    "target_args=()",
    "if [ -n \"$build_target\" ]; then target_args=(--target \"$build_target\"); fi",
    `docker buildx build "\${builder_args[@]}" --load "\${cache_args[@]}" "\${base_args[@]}" ${[...proxyBuildArgs, ...labelArgs].map(shellQuote).join(" ")} "\${compose_build_args[@]}" "\${target_args[@]}" --progress=plain -t "$image" -f "$dockerfile_abs" "$build_context"`,
    "docker image inspect \"$image\" --format 'image_id={{.Id}} labels={{json .Config.Labels}}'",
  ].filter((line) => line.length > 0).join("\n");
}

export function imageLabelVerifyScript(service: UniDeskMicroserviceConfig, expectedCommit: string): string {
  return [
    "set -euo pipefail",
    `container=${shellQuote(service.repository.containerName)}`,
    `expected=${shellQuote(expectedCommit)}`,
    "cid=$(docker ps -q -f name=\"^/${container}$\" | head -1)",
    "test -n \"$cid\"",
    "image_id=$(docker inspect -f '{{.Image}}' \"$cid\")",
    "actual=$(docker image inspect -f '{{ index .Config.Labels \"unidesk.ai/source-commit\" }}' \"$image_id\")",
    "test \"$actual\" = \"$expected\"",
    "printf 'container=%s image_id=%s deploy_commit=%s\\n' \"$container\" \"$image_id\" \"$actual\"",
  ].join("\n");
}

export function directDeployEnvPrefix(service: UniDeskMicroserviceConfig): string {
  return `UNIDESK_${service.id.replace(/[^A-Za-z0-9]+/gu, "_").replace(/^_+|_+$/gu, "").toUpperCase()}_DEPLOY`;
}

export function directComposeDeployEnv(service: UniDeskMicroserviceConfig, desired: DeployManifestService, resolvedCommit: string): Record<string, string> {
  if (!isDirectComposeDeployMode(service)) return {};
  const prefix = directDeployEnvPrefix(service);
  return {
    [`${prefix}_SERVICE_ID`]: service.id,
    [`${prefix}_REPO`]: desired.repo,
    [`${prefix}_COMMIT`]: resolvedCommit,
    [`${prefix}_REQUESTED_COMMIT`]: desired.commitId,
  };
}

export function composeEnvStampLines(service: UniDeskMicroserviceConfig, desired: DeployManifestService, resolvedCommit: string): string[] {
  const deployEnv = directComposeDeployEnv(service, desired, resolvedCommit);
  const entries = Object.entries(deployEnv);
  if (entries.length === 0) return [];
  const encoded = Buffer.from(JSON.stringify(deployEnv), "utf8").toString("base64");
  return [
    `deploy_env_b64=${shellQuote(encoded)}`,
    "if [ -n \"$compose_env_file\" ]; then",
    "  python3 - \"$compose_env_file\" \"$deploy_env_b64\" <<'PY'",
    "import base64, json, re, sys",
    "path = sys.argv[1]",
    "updates = json.loads(base64.b64decode(sys.argv[2]).decode('utf-8'))",
    "def env_value(value):",
    "    text = str(value)",
    "    return text if re.match(r'^[A-Za-z0-9_./:@-]+$', text) else json.dumps(text)",
    "try:",
    "    lines = open(path, encoding='utf-8').read().splitlines()",
    "except FileNotFoundError:",
    "    lines = []",
    "seen = set()",
    "out = []",
    "for line in lines:",
    "    if '=' not in line or line.startswith('#'):",
    "        out.append(line)",
    "        continue",
    "    key = line.split('=', 1)[0]",
    "    if key in updates:",
    "        out.append(f'{key}={env_value(updates[key])}')",
    "        seen.add(key)",
    "    else:",
    "        out.append(line)",
    "for key, value in updates.items():",
    "    if key not in seen:",
    "        out.append(f'{key}={env_value(value)}')",
    "with open(path, 'w', encoding='utf-8') as handle:",
    "    handle.write('\\n'.join(out) + '\\n')",
    "PY",
    "  echo compose_env_deploy_stamp=updated",
    "fi",
  ];
}

export function composeDeployScript(service: UniDeskMicroserviceConfig, desired: DeployManifestService, resolvedCommit: string): string {
  return [
    "set -euo pipefail",
    directComposeResolveScript(service),
    ...composeEnvStampLines(service, desired, resolvedCommit),
    "docker compose \"${compose_env_args[@]}\" -f \"$compose_file\" -p \"$project\" up -d --no-build --no-deps --force-recreate \"$compose_service\"",
    "ready=0",
    "for attempt in $(seq 1 90); do",
    "  cid=$(docker ps -q -f name=\"^/${container}$\" | head -1)",
    "  if [ -n \"$cid\" ]; then",
    "    health=$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' \"$cid\" 2>/dev/null || true)",
    "    echo compose_container_probe container=$container attempt=$attempt health=$health",
    "    if [ \"$health\" = \"healthy\" ] || [ \"$health\" = \"running\" ]; then ready=1; break; fi",
    "  else",
    "    echo compose_container_probe container=$container attempt=$attempt cid=missing",
    "  fi",
    "  sleep 1",
    "done",
    "test \"$ready\" = \"1\"",
  ].join("\n");
}

export function writeComposeEnvFallbackPath(): string {
  return rootPath(".state", "docker-compose.env");
}

export function rootAccessPrelude(): string[] {
  return [
    "root_exec() {",
    "  if [ \"$(id -u)\" = \"0\" ]; then \"$@\"; return; fi",
    "  if sudo -n true >/dev/null 2>&1; then sudo -n \"$@\"; return; fi",
    "  if [ -x /mnt/c/Windows/System32/wsl.exe ]; then /mnt/c/Windows/System32/wsl.exe -u root -- \"$@\"; return; fi",
    "  echo 'native_k3s_root_access=missing' >&2",
    "  return 1",
    "}",
    "root_shell() {",
    "  script=\"$1\"",
    "  if [ \"$(id -u)\" = \"0\" ]; then bash -lc \"$script\"; return; fi",
    "  if sudo -n true >/dev/null 2>&1; then sudo -n bash -lc \"$script\"; return; fi",
    "  if [ -x /mnt/c/Windows/System32/wsl.exe ]; then /mnt/c/Windows/System32/wsl.exe -u root -- bash -lc \"$script\"; return; fi",
    "  echo 'native_k3s_root_access=missing' >&2",
    "  return 1",
    "}",
  ];
}

export function ensureNativeK3sScript(): string {
  const installCommand = [
    "INSTALL_K3S_SKIP_DOWNLOAD=true",
    `INSTALL_K3S_VERSION=${nativeK3sInstallVersion}`,
    "INSTALL_K3S_EXEC=\"server --disable traefik --disable servicelb --disable metrics-server --node-name D601 --node-label unidesk.ai/node-id=D601 --node-label unidesk.ai/provider-id=D601 --tls-san 127.0.0.1 --tls-san host.docker.internal --write-kubeconfig-mode 644\"",
    "sh /tmp/unidesk-install-k3s.sh",
  ].join(" ");
  return [
    "set -euo pipefail",
    ...rootAccessPrelude(),
    `native_k3s_image=${shellQuote(nativeK3sImage)}`,
    `native_ctr_address=${shellQuote(nativeK3sCtrAddress)}`,
    `build_proxy=${shellQuote(providerGatewayWsEgressProxyUrl)}`,
    "configure_provider_deploy_proxy() {",
    "  export DOCKER_CONFIG=/tmp/unidesk-docker-config-clean",
    "  mkdir -p \"$DOCKER_CONFIG\"",
    "  [ -f \"$DOCKER_CONFIG/config.json\" ] || printf '{}' > \"$DOCKER_CONFIG/config.json\"",
    "  export HTTP_PROXY=\"$build_proxy\" HTTPS_PROXY=\"$build_proxy\" ALL_PROXY=\"$build_proxy\"",
    "  export NO_PROXY=\"localhost,127.0.0.1,::1,host.docker.internal\"",
    "  if ! curl -fsSI --max-time 20 -x \"$build_proxy\" https://github.com >/dev/null; then",
    "    echo native_k3s_provider_egress_proxy_unavailable=$build_proxy >&2",
    "    exit 1",
    "  fi",
    "  echo native_k3s_provider_egress_proxy=provider-gateway-ws-egress:$build_proxy",
    "}",
    "install_native_k3s_binaries() {",
    "  missing=0",
    "  for binary in k3s ctr containerd containerd-shim-runc-v2 crictl runc cni flannel bridge host-local loopback portmap bandwidth firewall; do",
    "    command -v \"$binary\" >/dev/null 2>&1 || missing=1",
    "  done",
    "  if [ \"$missing\" = \"0\" ]; then return; fi",
    "  docker image inspect \"$native_k3s_image\" >/dev/null 2>&1 || docker pull \"$native_k3s_image\"",
    "  tmp_dir=$(mktemp -d)",
    "  tmp_container=$(docker create \"$native_k3s_image\" sh -lc true)",
    "  docker cp \"$tmp_container:/bin/.\" \"$tmp_dir/bin\"",
    "  docker rm \"$tmp_container\" >/dev/null",
    "  for binary in k3s ctr containerd containerd-shim-runc-v2 crictl runc cni flannel bridge host-local loopback portmap bandwidth firewall; do",
    "    if [ -e \"$tmp_dir/bin/$binary\" ]; then",
    "      root_exec install -m 755 \"$tmp_dir/bin/$binary\" \"/usr/local/bin/$binary\"",
    "      echo native_k3s_binary_installed=$binary",
    "    fi",
    "  done",
    "  rm -rf \"$tmp_dir\"",
    "}",
    "unmount_invalid_wsl_kubelet_mounts() {",
    "  if awk 'NF != 6 && $0 ~ /\\/Docker\\/host/ {found=1} END {exit found ? 0 : 1}' /proc/mounts; then",
    "    root_exec umount /Docker/host >/dev/null 2>&1 || root_exec umount -l /Docker/host >/dev/null 2>&1 || true",
    "    echo native_k3s_unmounted_invalid_mount=/Docker/host",
    "  fi",
    "  awk 'NF != 6 {print \"native_k3s_invalid_mount_line=\" NR \":\" $0; bad=1} END {exit bad}' /proc/mounts",
    "}",
    "install_system_images_from_legacy_k3s() {",
    "  legacy_container=$(docker ps --format '{{.Names}} {{.Image}}' | awk '$2 ~ /^rancher\\/k3s:/ {print $1; exit}')",
    "  if [ -n \"$legacy_container\" ]; then",
    "    if docker exec \"$legacy_container\" ctr -n k8s.io images export /tmp/unidesk-k3s-system-images.tar docker.io/rancher/local-path-provisioner:v0.0.32 docker.io/rancher/mirrored-coredns-coredns:1.12.3 >/dev/null 2>&1; then",
    "      docker cp \"$legacy_container:/tmp/unidesk-k3s-system-images.tar\" /tmp/unidesk-k3s-system-images.tar >/dev/null",
    "      root_exec ctr --address \"$native_ctr_address\" -n k8s.io images import /tmp/unidesk-k3s-system-images.tar >/dev/null",
    "      echo native_k3s_imported_legacy_system_images=$legacy_container",
    "    fi",
    "  fi",
    "}",
    "install_pause_image() {",
    "  pause_image=rancher/mirrored-pause:3.6",
    "  pause_entrypoint=$(docker image inspect \"$pause_image\" --format '{{json .Config.Entrypoint}}' 2>/dev/null || true)",
    "  if ! printf '%s' \"$pause_entrypoint\" | grep -q '\"/pause\"'; then",
    "    docker image rm \"$pause_image\" >/dev/null 2>&1 || true",
    "    docker pull --platform linux/amd64 \"$pause_image\"",
    "    pause_entrypoint=$(docker image inspect \"$pause_image\" --format '{{json .Config.Entrypoint}}' 2>/dev/null || true)",
    "  fi",
    "  if ! printf '%s' \"$pause_entrypoint\" | grep -q '\"/pause\"'; then",
    "    echo native_k3s_pause_image_invalid_entrypoint=$pause_entrypoint >&2",
    "    exit 1",
    "  fi",
    "  docker save \"$pause_image\" -o /tmp/unidesk-k3s-pause.tar",
    "  root_exec ctr --address \"$native_ctr_address\" -n k8s.io images rm docker.io/rancher/mirrored-pause:3.6 >/dev/null 2>&1 || true",
    "  root_exec ctr --address \"$native_ctr_address\" -n k8s.io images import /tmp/unidesk-k3s-pause.tar >/dev/null",
    "  root_exec ctr --address \"$native_ctr_address\" -n k8s.io images ls | grep -F 'docker.io/rancher/mirrored-pause:3.6' >/dev/null",
    "  echo native_k3s_imported_pause_image=rancher/mirrored-pause:3.6",
    "}",
    "configure_provider_deploy_proxy",
    "install_native_k3s_binaries",
    "unmount_invalid_wsl_kubelet_mounts",
    "if ! systemctl cat k3s.service >/dev/null 2>&1; then",
    "  curl -fsSL --max-time 60 https://get.k3s.io -o /tmp/unidesk-install-k3s.sh",
    `  root_shell ${shellQuote(installCommand)}`,
    "fi",
    "if ! systemctl is-active --quiet k3s; then",
    "  root_exec systemctl enable --now k3s",
    "fi",
    "for attempt in $(seq 1 60); do",
    `  if KUBECONFIG=${shellQuote(k8sKubeconfig)} kubectl get nodes >/dev/null 2>&1; then break; fi`,
    "  sleep 2",
    "done",
    k3sTargetGuardScript(),
    `KUBECONFIG=${shellQuote(k8sKubeconfig)} kubectl get nodes -l unidesk.ai/node-id=D601 --no-headers | grep -q .`,
    `KUBECONFIG=${shellQuote(k8sKubeconfig)} kubectl wait --for=condition=Ready node -l unidesk.ai/node-id=D601 --timeout=180s`,
    "install_system_images_from_legacy_k3s",
    "install_pause_image",
    `KUBECONFIG=${shellQuote(k8sKubeconfig)} kubectl -n kube-system delete pod -l k8s-app=kube-dns --ignore-not-found >/dev/null 2>&1 || true`,
    `KUBECONFIG=${shellQuote(k8sKubeconfig)} kubectl -n kube-system delete pod -l app=local-path-provisioner --ignore-not-found >/dev/null 2>&1 || true`,
    "legacy_k3s_containers=$(docker ps --format '{{.Names}} {{.Image}}' | awk '$2 ~ /^rancher\\/k3s:/ {print $1}')",
    "for container in $legacy_k3s_containers; do",
    "  docker update --restart=no \"$container\" >/dev/null 2>&1 || true",
    "  docker stop \"$container\" >/dev/null",
    "  echo stopped_containerized_k3s=$container",
    "done",
    `KUBECONFIG=${shellQuote(k8sKubeconfig)} kubectl get nodes -o wide`,
    "printf 'native_k3s=ready kubeconfig=%s\\n' /etc/rancher/k3s/k3s.yaml",
  ].join("\n");
}

export function importK3sImageScript(service: UniDeskMicroserviceConfig): string {
  const image = buildImageTag(service);
  const archive = `/tmp/unidesk-${safeId(service.id)}-k3s-image.tar`;
  const manifest = `${targetWorkDir(service)}/${k8sManifestPath(service)}`;
  const codeQueueManifestPreflight = service.id === "code-queue"
    ? [
      "if [ -f \"$manifest\" ]; then",
      "  bad_manifest_images=$(python3 - \"$manifest\" \"$image\" <<'PY'",
      "import re, sys",
      "path, expected = sys.argv[1], sys.argv[2]",
      "required = {'code-queue','code-queue-read','code-queue-write','d601-provider-egress-proxy','d601-tcp-egress-gateway'}",
      "text = open(path, encoding='utf-8').read()",
      "bad = []",
      "seen = set()",
      "for doc in re.split(r'(?m)^---\\s*$', text):",
      "    if not re.search(r'(?m)^kind:\\s*Deployment\\s*$', doc):",
      "        continue",
      "    match = re.search(r'(?ms)^metadata:\\s*$.*?^  name:\\s*([A-Za-z0-9_.-]+)\\s*$', doc)",
      "    name = match.group(1) if match else ''",
      "    if name not in required:",
      "        continue",
      "    seen.add(name)",
      "    images = re.findall(r'(?m)^\\s*image:\\s*\"?([^\"\\s#]+)\"?', doc)",
      "    if not images or any(image != expected for image in images):",
      "        found = ','.join(images) if images else '<none>'",
      "        bad.append(f'{name}:{found}')",
      "missing = sorted(required - seen)",
      "bad.extend(f'{name}:<missing>' for name in missing)",
      "print('\\n'.join(bad))",
      "PY",
      "  )",
      "  if [ -n \"$bad_manifest_images\" ]; then",
      "    printf 'code_queue_manifest_image_preflight_failed image=%s\\n%s\\n' \"$image\" \"$bad_manifest_images\" >&2",
      "    exit 1",
      "  fi",
      "  echo code_queue_manifest_image_preflight=ok image=$image",
      "fi",
    ]
    : [];
  return [
    "set -euo pipefail",
    ...rootAccessPrelude(),
    `image=${shellQuote(image)}`,
    `archive=${shellQuote(archive)}`,
    `manifest=${shellQuote(manifest)}`,
    "docker image inspect \"$image\" >/dev/null",
    ...codeQueueManifestPreflight,
    "rm -f \"$archive\"",
    "docker save \"$image\" -o \"$archive\"",
    `root_exec ctr --address ${shellQuote(nativeK3sCtrAddress)} -n k8s.io images import "$archive"`,
    "rm -f \"$archive\"",
    `if ! root_exec ctr --address ${shellQuote(nativeK3sCtrAddress)} -n k8s.io images ls | grep -F "$image"; then`,
    "  printf 'native_k3s_containerd_image_missing=%s\\n' \"$image\" >&2",
    "  exit 1",
    "fi",
    "printf 'native_k3s_containerd_image_present=%s\\n' \"$image\"",
  ].join("\n");
}
