// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. public-exposure module for scripts/src/hwlab-node-impl.ts.

// Moved mechanically from scripts/src/hwlab-node-impl.ts:11201-12084 for #903.

// SPEC: PJ2026-01060505 Workbench Performance draft-2026-06-17-p0.
// SPEC: PJ2026-01060508 Web哨兵 draft-2026-06-25-p0-web-probe-sentinel.
// Responsibility: YAML-first node/lane operations, including Workbench observability control commands.
import { createHash, randomBytes } from "node:crypto";
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { repoRoot, rootPath, type Config } from "../config";
import { runCommand, type CommandResult } from "../command";
import { startJob } from "../jobs";
import { classifySshTcpPoolFailure } from "../ssh";
import { HWLAB_NODE_CONTROL_PLANE_CONFIG_PATH, hwlabNodeControlPlaneInfraHelp, runHwlabNodeControlPlaneInfra } from "../hwlab-node-control-plane";
import { hwlabRuntimeLaneConfigPath, hwlabRuntimeLaneIds, hwlabRuntimeLaneSpec, hwlabRuntimeLaneSpecForNode, hwlabRuntimeNodeIds, isHwlabRuntimeLane, type HwlabRuntimeLane, type HwlabRuntimeLaneSpec, type HwlabRuntimeObservabilityRecordingRuleSpec, type HwlabRuntimeObservabilitySpec, type HwlabRuntimeObservabilityWarningAlertSpec, type HwlabRuntimePk01PublicExposureSpec, type HwlabRuntimePublicExposureSpec, type HwlabRuntimeWebProbeAlertThresholdsSpec, type HwlabRuntimeWebProbeProjectManagementSpec } from "../hwlab-node-lanes";
import { nodeWebProbeScriptRunnerSource } from "../hwlab-node-web-probe-runner-source";
import { nodeWebObserveAnalyzerSource } from "../hwlab-node-web-observe-analyzer-source";
import { nodeWebObserveRunnerSource } from "../hwlab-node-web-observe-runner-source";
import { nodeWebObserveCollectViewNodeScript, parseNodeWebProbeObserveCollectView, type NodeWebProbeObserveCollectView } from "../hwlab-node-web-observe-collect";
import { withWebObserveCollectRendered, withWebObserveCommandRendered, withWebObserveStatusRendered } from "../hwlab-node-web-observe-render";
import { buildWebObserveWrapperForObserveOptions, webObserveWrapperStateDirFromStatus } from "../hwlab-node-web-observe-wrapper";
import { renderWebObserveWrapperContract } from "../hwlab-node-web-observe-wrapper-render";
import { runWebProbeSentinelCommand, type WebProbeSentinelOptions } from "../hwlab-node-web-sentinel-cicd";
import { hwlabNodeHelp, hwlabNodeObservabilityHelp, hwlabNodeWebProbeHelp } from "../hwlab-node-help";
import { compactWebProbeResult, compactWebProbeScriptResult } from "../hwlab-node-web-probe-summary";
import { nodeObservabilityRecordingRuleExpression, nodeObservabilityRecordingRuleSummaries, nodeObservabilityWarningAlertExpression, nodeObservabilityWarningAlertSummaries } from "../hwlab-node-observability-promql";
import { runDelegatedHwlabNodeCommand, type DelegatedNodeDomain } from "../hwlab-node-transport";
import type { RenderedCliResult } from "../output";

import type { CodeAgentProviderSecretMaterial, NodePublicExposureOptions, NodeSecretOptions, RuntimeSecretSpec } from "./entry";
import { BOOTSTRAP_ADMIN_PASSWORD_HASH_KEY, BOOTSTRAP_ADMIN_SOURCE_NAMESPACE, BOOTSTRAP_ADMIN_SOURCE_SECRET, CLOUD_API_DB_KEY, CODE_AGENT_PROVIDER_OPENAI_KEY, CODE_AGENT_PROVIDER_OPENCODE_KEY, CODE_AGENT_PROVIDER_SOURCE_NAMESPACE, CODE_AGENT_PROVIDER_SOURCE_SECRET, MASTER_ADMIN_API_KEY_KEY, OPENFGA_AUTHN_KEY, OPENFGA_DATASTORE_URI_KEY, OPENFGA_POSTGRES_PASSWORD_KEY } from "./entry";
import { transPath } from "./runtime-common";
import { bootstrapAdminSecretScript, cloudApiDbSecretScript, codeAgentProviderSecretScript, masterAdminApiKeySecretScript, obsoletePlatformDbCleanupScript, obsoletePlatformDbStatusFromText, obsoleteSecretCleanupScript, openFgaSecretScript, ownedPostgresCleanupScript, platformDbSecretStatusScript, secretStatusFromText } from "./secret-scripts";
import { assertLane, assertNodeId, compactCommandResult, keyValueLinesFromText, numericField, optionValue, positiveIntegerOption, requiredOption, shellQuote, statusText } from "./utils";
import { displayRepoPath, localSecretSourcePaths, parseEnvFile, readBootstrapAdminSecretMaterial, shortSecretFingerprint, syncNodeExternalPostgresSecrets } from "./web-probe";
import { hwlabRuntimeActiveExternalPostgres } from "../hwlab-node-lanes";

export function isSafeWebProbeScriptRunDir(value: string | null): value is string {
  return typeof value === "string"
    && value.length > 0
    && !value.includes("\0")
    && !value.includes("..")
    && /\.state\/web-probe-script\/run\.[A-Za-z0-9]+$/u.test(value);
}

export function isSafeWebProbeScriptReportPath(value: string | null): value is string {
  return typeof value === "string"
    && isSafeWebProbeScriptArtifactPath(value)
    && value.endsWith("/web-probe-script-report.json");
}

export function isSafeWebProbeScriptArtifactPath(value: string): boolean {
  return typeof value === "string"
    && value.length > 0
    && !value.includes("\0")
    && !value.includes("..")
    && /\.state\/web-probe-script\/run\.[A-Za-z0-9]+\/[^/]+[.](?:png|json)$/u.test(value);
}

export function parseSecretOptions(args: string[]): NodeSecretOptions {
  const [actionRaw] = args;
  if (actionRaw !== "status" && actionRaw !== "ensure" && actionRaw !== "cleanup-owned-postgres" && actionRaw !== "cleanup-obsolete") {
    throw new Error("secret usage: status|ensure --node NODE --lane LANE --name SECRET [--dry-run|--confirm] | cleanup-owned-postgres --node NODE --lane LANE [--dry-run|--confirm] | cleanup-obsolete --node NODE --lane LANE --name SECRET [--dry-run|--confirm]");
  }
  const node = requiredOption(args, "--node");
  assertNodeId(node);
  const lane = requiredOption(args, "--lane");
  assertLane(lane);
  const spec = runtimeSecretSpec({ node, lane });
  const name = optionValue(args, "--name") ?? spec.openFgaSecret;
  const key = optionValue(args, "--key");
  const confirm = args.includes("--confirm");
  const explicitDryRun = args.includes("--dry-run");
  if (confirm && explicitDryRun) throw new Error("secret accepts only one of --confirm or --dry-run");
  if (actionRaw === "cleanup-owned-postgres") {
    if (lane === "v02") throw new Error("secret cleanup-owned-postgres is only for v0.3+ lanes after migration to G14 platform Postgres");
    if (key !== undefined) throw new Error("secret cleanup-owned-postgres does not accept --key");
    if (name !== spec.openFgaSecret && name !== spec.postgresSecret) throw new Error(`secret cleanup-owned-postgres for --lane ${lane} targets ${spec.postgresSecret}; omit --name or pass --name ${spec.postgresSecret}`);
    return {
      action: actionRaw,
      node,
      lane,
      name: spec.postgresSecret,
      preset: "owned-postgres-cleanup",
      confirm,
      dryRun: explicitDryRun || !confirm,
      timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", 180, 900),
    };
  }
  if (actionRaw === "cleanup-obsolete") {
    if (lane === "v02") throw new Error("secret cleanup-obsolete is only for v0.3+ lanes after deprecated v0.3 HWPOD DB SecretRef cleanup");
    if (key !== undefined) throw new Error("secret cleanup-obsolete does not accept --key");
    if (name !== spec.obsoleteHwpodDbSecret) throw new Error(`secret cleanup-obsolete for --lane ${lane} currently only targets obsolete ${spec.obsoleteHwpodDbSecret}`);
    return {
      action: actionRaw,
      node,
      lane,
      name,
      preset: "obsolete-secret-cleanup",
      confirm,
      dryRun: explicitDryRun || !confirm,
      timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", 180, 900),
    };
  }
  if (name === spec.masterAdminApiKeySecret) {
    if (key !== undefined && key !== MASTER_ADMIN_API_KEY_KEY) throw new Error(`secret ${name} supports only key ${MASTER_ADMIN_API_KEY_KEY}`);
    return {
      action: actionRaw,
      node,
      lane,
      name,
      key: key ?? MASTER_ADMIN_API_KEY_KEY,
      preset: "master-server-admin-api-key",
      confirm,
      dryRun: actionRaw === "status" ? true : explicitDryRun || !confirm,
      timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", 180, 900),
    };
  }
  if (name === spec.bootstrapAdminSecret) {
    if (key !== undefined && key !== BOOTSTRAP_ADMIN_PASSWORD_HASH_KEY) throw new Error(`secret ${name} supports only key ${BOOTSTRAP_ADMIN_PASSWORD_HASH_KEY}`);
    const force = args.includes("--force");
    if (force && actionRaw !== "ensure") throw new Error("secret --force is only supported with ensure");
    return {
      action: actionRaw,
      node,
      lane,
      name,
      key: key ?? BOOTSTRAP_ADMIN_PASSWORD_HASH_KEY,
      preset: "bootstrap-admin",
      confirm,
      force,
      dryRun: actionRaw === "status" ? true : explicitDryRun || !confirm,
      timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", 180, 900),
    };
  }
  if (name === spec.codeAgentProviderSecret) {
    if (key !== undefined && key !== CODE_AGENT_PROVIDER_OPENAI_KEY && key !== CODE_AGENT_PROVIDER_OPENCODE_KEY) {
      throw new Error(`secret ${name} supports keys ${CODE_AGENT_PROVIDER_OPENAI_KEY} and ${CODE_AGENT_PROVIDER_OPENCODE_KEY}`);
    }
    return {
      action: actionRaw,
      node,
      lane,
      name,
      key,
      preset: "code-agent-provider",
      confirm,
      dryRun: actionRaw === "status" ? true : explicitDryRun || !confirm,
      timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", 180, 900),
    };
  }
  if (name === spec.cloudApiDbSecret) {
    if (key !== undefined && key !== spec.cloudApiDbKey) throw new Error(`secret ${name} supports only key ${spec.cloudApiDbKey}`);
    if (actionRaw === "ensure" && spec.platformDb && spec.externalPostgres === undefined) {
      throw new Error(`secret ensure for ${name} on --lane ${lane} was removed after native platform DB migration; use status plus platform DB SecretRef rotation CLI when it exists`);
    }
    return {
      action: actionRaw,
      node,
      lane,
      name,
      key: key ?? spec.cloudApiDbKey,
      preset: "cloud-api-db",
      confirm,
      dryRun: actionRaw === "status" ? true : explicitDryRun || !confirm,
      timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", 240, 900),
    };
  }
  if (name !== spec.openFgaSecret) {
    throw new Error(`secret status/ensure supports --name ${spec.openFgaSecret}, ${spec.masterAdminApiKeySecret}, ${spec.bootstrapAdminSecret}, ${spec.cloudApiDbSecret}, or ${spec.codeAgentProviderSecret} for --lane ${lane}; obsolete ${spec.obsoleteHwpodDbSecret} must use cleanup-obsolete`);
  }
  if (key !== undefined && key !== OPENFGA_AUTHN_KEY && key !== OPENFGA_DATASTORE_URI_KEY && key !== OPENFGA_POSTGRES_PASSWORD_KEY) {
    throw new Error(`secret ${name} supports keys ${OPENFGA_AUTHN_KEY}, ${OPENFGA_DATASTORE_URI_KEY}, and ${OPENFGA_POSTGRES_PASSWORD_KEY}`);
  }
  if (actionRaw === "ensure" && spec.platformDb && spec.externalPostgres === undefined) {
    throw new Error(`secret ensure for ${name} on --lane ${lane} was removed after native platform DB migration; use status plus platform DB SecretRef rotation CLI when it exists`);
  }
  return {
    action: actionRaw,
    node,
    lane,
    name,
    key,
    preset: "openfga",
    confirm,
    dryRun: actionRaw === "status" ? true : explicitDryRun || !confirm,
    timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", 180, 900),
  };
}

export function runtimeSecretSpec(input: { node: string; lane: string }): RuntimeSecretSpec {
  const namespace = `hwlab-${input.lane}`;
  const runtimeLaneSpec = isHwlabRuntimeLane(input.lane) ? hwlabRuntimeLaneSpecForNode(input.lane, input.node) : undefined;
  const externalPostgres = runtimeLaneSpec === undefined ? undefined : hwlabRuntimeActiveExternalPostgres(runtimeLaneSpec);
  const postgresStore = runtimeLaneSpec?.runtimeStore?.postgres;
  const bootstrapAdmin = runtimeLaneSpec?.bootstrapAdmin;
  const platformDb = postgresStore?.mode === "platform-service";
  const localPostgresService = postgresStore?.serviceName ?? `${namespace}-postgres`;
  const platformPostgresService = externalPostgres?.serviceName ?? postgresStore?.serviceName ?? "g14-platform-postgres";
  const platformPostgresRuntimeAccess = externalPostgres?.runtimeAccess;
  const legacyPostgresHost = `${localPostgresService}.${namespace}.svc.cluster.local`;
  const platformPostgresHost = `${platformPostgresService}.${namespace}.svc.cluster.local`;
  const platformPostgresEndpointSlice = `${platformPostgresService}-host`;
  const openFgaDbName = externalPostgres?.database ?? postgresStore?.openfga?.database ?? (platformDb ? `openfga_${input.lane}` : "hwlab_openfga");
  const openFgaDbUser = externalPostgres?.openfga.role ?? postgresStore?.openfga?.role ?? (platformDb ? `openfga_${input.lane}_app` : "hwlab_openfga");
  const cloudApiDbName = externalPostgres?.database ?? postgresStore?.cloudApi?.database ?? `hwlab_${input.lane}`;
  const cloudApiDbUser = externalPostgres?.cloudApi.role ?? postgresStore?.cloudApi?.role ?? (platformDb ? `hwlab_${input.lane}_app` : `hwlab_${input.lane}`);
  return {
    node: input.node,
    lane: input.lane,
    namespace,
    platformDb,
    ...(runtimeLaneSpec === undefined ? {} : { runtimeLaneSpec }),
    ...(externalPostgres === undefined ? {} : { externalPostgres }),
    platformPostgresService,
    platformPostgresEndpointAddress: platformPostgresRuntimeAccess?.endpointAddress ?? externalPostgres?.endpointAddress,
    platformPostgresEndpointSlice,
    postgresSecret: postgresStore?.secretName ?? `${namespace}-postgres`,
    postgresStatefulSet: postgresStore?.statefulSet ?? postgresStore?.secretName ?? `${namespace}-postgres`,
    postgresAdminUser: postgresStore?.adminUser ?? `hwlab_${input.lane}`,
    openFgaSecret: externalPostgres?.openfga.secretName ?? postgresStore?.openfga?.secretName ?? `${namespace}-openfga`,
    openFgaDbName,
    openFgaDbUser,
    openFgaDbHost: platformDb ? platformPostgresHost : legacyPostgresHost,
    masterAdminApiKeySecret: `${namespace}-master-server-admin-api-key`,
    ...(runtimeLaneSpec?.codeAgentRuntime?.apiKeySourceRef === undefined ? {} : { masterAdminApiKeySourceRef: runtimeLaneSpec.codeAgentRuntime.apiKeySourceRef }),
    ...(runtimeLaneSpec?.codeAgentRuntime?.apiKeySourceKey === undefined ? {} : { masterAdminApiKeySourceKey: runtimeLaneSpec.codeAgentRuntime.apiKeySourceKey }),
    bootstrapAdminSecret: bootstrapAdmin?.secretName ?? `${namespace}-bootstrap-admin`,
    bootstrapAdminPasswordHashKey: bootstrapAdmin?.secretKey ?? BOOTSTRAP_ADMIN_PASSWORD_HASH_KEY,
    bootstrapAdminUsername: bootstrapAdmin?.username ?? "admin",
    bootstrapAdminDisplayName: bootstrapAdmin?.displayName ?? `HWLAB ${input.lane} Admin`,
    ...(bootstrapAdmin?.usernameSourceRef === undefined ? {} : { bootstrapAdminUsernameSourceRef: bootstrapAdmin.usernameSourceRef }),
    ...(bootstrapAdmin?.usernameSourceKey === undefined ? {} : { bootstrapAdminUsernameSourceKey: bootstrapAdmin.usernameSourceKey }),
    ...(bootstrapAdmin?.usernameSourceLine === undefined ? {} : { bootstrapAdminUsernameSourceLine: bootstrapAdmin.usernameSourceLine }),
    ...(bootstrapAdmin?.passwordSourceRef === undefined ? {} : { bootstrapAdminPasswordSourceRef: bootstrapAdmin.passwordSourceRef }),
    ...(bootstrapAdmin?.passwordSourceKey === undefined ? {} : { bootstrapAdminPasswordSourceKey: bootstrapAdmin.passwordSourceKey }),
    ...(bootstrapAdmin?.passwordSourceLine === undefined ? {} : { bootstrapAdminPasswordSourceLine: bootstrapAdmin.passwordSourceLine }),
    ...(bootstrapAdmin?.passwordHashTransform === undefined ? {} : { bootstrapAdminPasswordHashTransform: bootstrapAdmin.passwordHashTransform }),
    bootstrapAdminSourceNamespace: BOOTSTRAP_ADMIN_SOURCE_NAMESPACE,
    bootstrapAdminSourceSecret: BOOTSTRAP_ADMIN_SOURCE_SECRET,
    cloudApiDbSecret: externalPostgres?.cloudApi.secretName ?? postgresStore?.cloudApi?.secretName ?? `hwlab-cloud-api-${input.lane}-db`,
    cloudApiDbKey: externalPostgres?.cloudApi.secretKey ?? postgresStore?.cloudApi?.secretKey ?? CLOUD_API_DB_KEY,
    cloudApiDbName,
    cloudApiDbUser,
    cloudApiDbHost: platformDb ? platformPostgresHost : legacyPostgresHost,
    cloudApiDeployment: "hwlab-cloud-api",
    obsoleteHwpodDbSecret: `hwpod-${input.lane}-db`,
    obsoleteHwpodDbName: `hwpod_${input.lane}`,
    obsoleteHwpodDbUser: `hwpod_${input.lane}_app`,
    codeAgentProviderSecret: runtimeLaneSpec?.codeAgentProvider?.secretName ?? `${namespace}-code-agent-provider`,
    codeAgentProviderSourceNamespace: CODE_AGENT_PROVIDER_SOURCE_NAMESPACE,
    codeAgentProviderSourceSecret: CODE_AGENT_PROVIDER_SOURCE_SECRET,
    ...(runtimeLaneSpec?.codeAgentProvider?.sourceRef === undefined ? {} : { codeAgentProviderSourceRef: runtimeLaneSpec.codeAgentProvider.sourceRef }),
    ...(runtimeLaneSpec?.codeAgentProvider?.openaiSourceKey === undefined ? {} : { codeAgentProviderOpenaiSourceKey: runtimeLaneSpec.codeAgentProvider.openaiSourceKey }),
    ...(runtimeLaneSpec?.codeAgentProvider?.opencodeSourceKey === undefined ? {} : { codeAgentProviderOpencodeSourceKey: runtimeLaneSpec.codeAgentProvider.opencodeSourceKey }),
    fieldManager: `unidesk-hwlab-node-${input.lane}-secret`,
  };
}

export function runNodeSecret(options: NodeSecretOptions): Record<string, unknown> {
  const spec = runtimeSecretSpec(options);
  if (options.preset === "obsolete-secret-cleanup") return runObsoleteSecretCleanup(options, spec);
  if (spec.externalPostgres !== undefined && options.action === "ensure" && (options.preset === "cloud-api-db" || options.preset === "openfga")) {
    return runExternalPostgresSecretEnsure(options, spec);
  }
  const bootstrapAdminMaterial = options.preset === "bootstrap-admin" ? readBootstrapAdminSecretMaterial(spec) : null;
  const codeAgentProviderMaterial = options.preset === "code-agent-provider" && spec.codeAgentProviderSourceRef !== undefined
    ? readCodeAgentProviderSecretMaterial(spec)
    : null;
  const input = options.preset === "master-server-admin-api-key" && options.action === "ensure" && !options.dryRun
    ? readMasterAdminApiKeyFromYaml(spec).key
    : options.preset === "bootstrap-admin" && options.action === "ensure" && !options.dryRun && bootstrapAdminMaterial?.ok === true
      ? bootstrapAdminMaterial.passwordHash ?? ""
    : "";
  const script = options.preset === "openfga"
    ? spec.platformDb ? platformDbSecretStatusScript(options, spec) : openFgaSecretScript(options, spec)
    : options.preset === "master-server-admin-api-key"
      ? masterAdminApiKeySecretScript(options, spec)
      : options.preset === "bootstrap-admin"
        ? bootstrapAdminSecretScript(options, spec, bootstrapAdminMaterial)
        : options.preset === "cloud-api-db"
          ? spec.platformDb ? platformDbSecretStatusScript(options, spec) : cloudApiDbSecretScript(options, spec)
          : options.preset === "owned-postgres-cleanup"
            ? ownedPostgresCleanupScript(options, spec)
            : codeAgentProviderSecretScript(options, spec, codeAgentProviderMaterial);
  const result = runTransScript(options.node, script, input, options.timeoutSeconds);
  const status = secretStatusFromText(statusText(result), result.exitCode === 0, result.exitCode, result.stderr, spec);
  const dryRunOk = options.action === "ensure" && options.dryRun && result.exitCode === 0;
  const cleanupDryRunOk = options.action === "cleanup-owned-postgres" && options.dryRun && result.exitCode === 0;
  const obsoleteCleanupDryRunOk = options.action === "cleanup-obsolete" && options.dryRun && status.ok === true;
  const ok = dryRunOk || cleanupDryRunOk || obsoleteCleanupDryRunOk ? true : status.ok === true;
  return {
    ok,
    command: `hwlab nodes secret ${options.action}`,
    node: options.node,
    lane: options.lane,
    namespace: spec.namespace,
    secret: options.name,
    key: options.key ?? null,
    preset: options.preset,
    mode: options.action === "status" ? "status" : options.dryRun ? "dry-run" : options.action === "cleanup-owned-postgres" || options.action === "cleanup-obsolete" ? "confirmed-delete" : "confirmed-ensure",
    status,
    mutation: status.mutation === true,
    result: compactCommandResult(result),
    valuesRedacted: true,
    next: ok && options.action === "status" ? undefined : nextSecretCommand(options, spec),
  };
}

function readMasterAdminApiKeyFromYaml(spec: RuntimeSecretSpec): { key: string; source: string } {
  const sourceRef = spec.masterAdminApiKeySourceRef;
  const sourceKey = spec.masterAdminApiKeySourceKey;
  if (sourceRef === undefined || sourceKey === undefined) {
    throw new Error(`config/hwlab-node-lanes.yaml#lanes.${spec.lane}.codeAgentRuntime must declare apiKeySourceRef and apiKeySourceKey`);
  }
  const source = localSecretSourcePaths(sourceRef).find((candidate) => existsSync(candidate)) ?? localSecretSourcePaths(sourceRef)[0];
  if (source === undefined || !existsSync(source)) throw new Error(`HWLAB_API_KEY source missing: ${sourceRef}`);
  const raw = parseEnvFile(readFileSync(source, "utf8"))[sourceKey]?.trim() ?? "";
  if (!raw.startsWith("hwl_live_")) throw new Error(`HWLAB_API_KEY source invalid: ${sourceRef}#${sourceKey}`);
  return { key: raw, source };
}

export function readCodeAgentProviderSecretMaterial(spec: RuntimeSecretSpec): CodeAgentProviderSecretMaterial {
  const sourceRef = spec.codeAgentProviderSourceRef ?? null;
  if (sourceRef === null) {
    return {
      ok: false,
      sourceRef,
      sourcePath: null,
      sourcePresent: false,
      openaiSourceKey: spec.codeAgentProviderOpenaiSourceKey ?? null,
      opencodeSourceKey: spec.codeAgentProviderOpencodeSourceKey ?? null,
      openaiValue: null,
      opencodeValue: null,
      openaiFingerprint: null,
      opencodeFingerprint: null,
      error: "code-agent-provider-source-ref-missing",
    };
  }
  const paths = localSecretSourcePaths(sourceRef);
  const sourcePath = paths.find((candidate) => existsSync(candidate)) ?? paths[0] ?? null;
  const openaiSourceKey = spec.codeAgentProviderOpenaiSourceKey ?? null;
  const opencodeSourceKey = spec.codeAgentProviderOpencodeSourceKey ?? null;
  if (sourcePath === null) {
    return {
      ok: false,
      sourceRef,
      sourcePath,
      sourcePresent: false,
      openaiSourceKey,
      opencodeSourceKey,
      openaiValue: null,
      opencodeValue: null,
      openaiFingerprint: null,
      opencodeFingerprint: null,
      error: "code-agent-provider-source-path-unresolved",
    };
  }
  if (!existsSync(sourcePath)) {
    return {
      ok: false,
      sourceRef,
      sourcePath: displayRepoPath(sourcePath),
      sourcePresent: false,
      openaiSourceKey,
      opencodeSourceKey,
      openaiValue: null,
      opencodeValue: null,
      openaiFingerprint: null,
      opencodeFingerprint: null,
      error: "code-agent-provider-source-missing",
    };
  }
  const values = parseEnvFile(readFileSync(sourcePath, "utf8"));
  const openaiValue = openaiSourceKey === null ? null : values[openaiSourceKey] ?? null;
  const opencodeValue = opencodeSourceKey === null ? null : values[opencodeSourceKey] ?? null;
  const ok = (openaiValue !== null && openaiValue.length > 0) || (opencodeValue !== null && opencodeValue.length > 0);
  return {
    ok,
    sourceRef,
    sourcePath: displayRepoPath(sourcePath),
    sourcePresent: true,
    openaiSourceKey,
    opencodeSourceKey,
    openaiValue,
    opencodeValue,
    openaiFingerprint: openaiValue === null || openaiValue.length === 0 ? null : shortSecretFingerprint(openaiValue),
    opencodeFingerprint: opencodeValue === null || opencodeValue.length === 0 ? null : shortSecretFingerprint(opencodeValue),
    error: ok ? null : "code-agent-provider-source-key-missing",
  };
}

export function nextSecretCommand(options: NodeSecretOptions, spec: RuntimeSecretSpec): Record<string, string> {
  if (options.action === "cleanup-owned-postgres") {
    return { ensure: `bun scripts/cli.ts hwlab nodes secret cleanup-owned-postgres --node ${options.node} --lane ${options.lane} --confirm` };
  }
  if (options.action === "cleanup-obsolete") {
    return { cleanup: `bun scripts/cli.ts hwlab nodes secret cleanup-obsolete --node ${options.node} --lane ${options.lane} --name ${options.name} --confirm` };
  }
  if (spec.externalPostgres !== undefined && (options.preset === "cloud-api-db" || options.preset === "openfga")) {
    return { ensure: `bun scripts/cli.ts hwlab nodes secret ensure --node ${options.node} --lane ${options.lane} --name ${options.name}${options.key ? ` --key ${options.key}` : ""} --confirm` };
  }
  if (spec.platformDb && (options.preset === "cloud-api-db" || options.preset === "openfga")) {
    return {
      status: `bun scripts/cli.ts hwlab nodes secret status --node ${options.node} --lane ${options.lane} --name ${options.name}${options.key ? ` --key ${options.key}` : ""}`,
      controlPlaneStatus: `bun scripts/cli.ts hwlab nodes control-plane status --node ${options.node} --lane ${options.lane}`,
    };
  }
  return { ensure: `bun scripts/cli.ts hwlab nodes secret ensure --node ${options.node} --lane ${options.lane} --name ${options.name}${options.key ? ` --key ${options.key}` : ""} --confirm` };
}

export function runExternalPostgresSecretEnsure(options: NodeSecretOptions, spec: RuntimeSecretSpec): Record<string, unknown> {
  const runtimeLaneSpec = spec.runtimeLaneSpec;
  if (runtimeLaneSpec === undefined) {
    return {
      ok: false,
      command: `hwlab nodes secret ${options.action}`,
      node: options.node,
      lane: options.lane,
      namespace: spec.namespace,
      secret: options.name,
      key: options.key ?? null,
      preset: options.preset,
      mode: options.dryRun ? "dry-run" : "confirmed-ensure",
      mutation: false,
      degradedReason: "external-postgres-runtime-lane-spec-missing",
      valuesRedacted: true,
    };
  }
  const sync = syncNodeExternalPostgresSecrets(runtimeLaneSpec, options.dryRun, options.timeoutSeconds);
  const shouldReadStatus = sync !== null && sync.ok === true;
  const statusResult = shouldReadStatus ? runTransScript(options.node, platformDbSecretStatusScript({ ...options, action: "status", dryRun: true }, spec), "", options.timeoutSeconds) : null;
  const status = statusResult === null
    ? null
    : secretStatusFromText(statusText(statusResult), statusResult.exitCode === 0, statusResult.exitCode, statusResult.stderr, spec);
  const ok = sync !== null && sync.ok === true && (options.dryRun || status?.ok === true);
  return {
    ok,
    command: `hwlab nodes secret ${options.action}`,
    node: options.node,
    lane: options.lane,
    namespace: spec.namespace,
    secret: options.name,
    key: options.key ?? null,
    preset: options.preset,
    mode: options.dryRun ? "dry-run" : "confirmed-ensure",
    status: status ?? sync,
    externalPostgresSecretSync: sync,
    mutation: sync?.mutation === true,
    result: statusResult === null ? null : compactCommandResult(statusResult),
    valuesRedacted: true,
    next: ok ? undefined : nextSecretCommand(options, spec),
  };
}

export function publicExposureSummary(exposure: HwlabRuntimePublicExposureSpec): Record<string, unknown> {
  if (exposure.mode === "node-port-shared-edge") {
    return {
      mode: exposure.mode,
      publicBaseUrl: exposure.publicBaseUrl,
      hostname: exposure.hostname,
      expectedA: exposure.expectedA,
      upstream: exposure.upstream,
      healthPath: exposure.healthPath,
      service: {
        name: exposure.serviceName,
        selector: exposure.selector,
        port: exposure.port,
        targetPort: exposure.targetPort,
        nodePort: exposure.nodePort,
      },
    };
  }
  return {
    mode: exposure.mode,
    publicBaseUrl: exposure.publicBaseUrl,
    hostname: exposure.hostname,
    expectedA: exposure.expectedA,
    frpc: {
      serverAddr: exposure.serverAddr,
      serverPort: exposure.serverPort,
      tokenSourceRef: exposure.tokenSourceRef,
      tokenSourceKey: exposure.tokenSourceKey,
      secretName: exposure.secretName,
      secretKey: exposure.secretKey,
      tokenKey: exposure.tokenKey,
      webProxy: exposure.webProxy,
      apiProxy: exposure.apiProxy,
      extraProxies: exposure.extraProxies,
    },
    caddy: {
      route: exposure.caddyRoute,
      configPath: exposure.caddyConfigPath,
      serviceName: exposure.caddyServiceName,
      tls: exposure.caddyTls,
      responseHeaderTimeoutSeconds: exposure.responseHeaderTimeoutSeconds,
    },
  };
}

export function runNodePublicExposure(options: NodePublicExposureOptions): Record<string, unknown> {
  const exposure = options.spec.publicExposure;
  if (exposure === null || !exposure.enabled) {
    return {
      ok: false,
      command: "hwlab nodes control-plane public-exposure",
      node: options.node,
      lane: options.lane,
      configPath: hwlabRuntimeLaneConfigPath(),
      error: "publicExposure is not configured for this node/lane target",
      mutation: false,
    };
  }
  if (exposure.mode === "node-port-shared-edge") {
    return {
      ok: true,
      command: "hwlab nodes control-plane public-exposure",
      node: options.node,
      lane: options.lane,
      mode: "gitops-managed-node-port",
      mutation: false,
      configPath: `${HWLAB_NODE_CONTROL_PLANE_CONFIG_PATH}#publicExposures.${options.node}.${options.lane}`,
      publicExposure: publicExposureSummary(exposure),
      valuesRedacted: true,
      next: {
        delivery: "merge the source PR and observe the automatic PaC/GitOps/Argo chain",
        status: `bun scripts/cli.ts hwlab nodes control-plane status --node ${options.node} --lane ${options.lane}`,
      },
    };
  }
  const source = readPublicExposureTokenSource(exposure);
  if (!source.ok) {
    return {
      ok: false,
      command: "hwlab nodes control-plane public-exposure",
      node: options.node,
      lane: options.lane,
      mode: options.dryRun ? "dry-run" : "confirmed",
      configPath: hwlabRuntimeLaneConfigPath(),
      publicExposure: publicExposureSummary(exposure),
      source,
      mutation: false,
      valuesRedacted: true,
      next: { fixSecretSource: `create ${rootPath(".state", "secrets", exposure.tokenSourceRef)} with ${exposure.tokenSourceKey}=<redacted>` },
    };
  }
  const caddyResult = runTransHostScript(exposure.caddyRoute, publicExposureCaddyScript(options, exposure), "", options.timeoutSeconds);
  const caddyFields = keyValueLinesFromText(statusText(caddyResult));
  const secretStatus = options.dryRun
    ? publicExposureSecretDryRunStatus(options, exposure, source.value?.length ?? 0)
    : publicExposureSecretApplyStatus(options, exposure, source.value ?? "");
  const caddyStatus = publicExposureCaddyStatus(caddyFields, caddyResult);
  const ok = secretStatus.ok === true && caddyStatus.ok === true;
  return {
    ok,
    command: "hwlab nodes control-plane public-exposure",
    node: options.node,
    lane: options.lane,
    mode: options.dryRun ? "dry-run" : "confirmed",
    mutation: !options.dryRun && (secretStatus.mutation === true || caddyFields.mutation === "true"),
    configPath: hwlabRuntimeLaneConfigPath(),
    publicExposure: publicExposureSummary(exposure),
    source: {
      ok: source.ok,
      path: source.path,
      key: exposure.tokenSourceKey,
      bytes: source.value?.length ?? 0,
      fingerprint: source.fingerprint,
      valueRedacted: true,
    },
    secret: secretStatus,
    caddy: caddyStatus,
    valuesRedacted: true,
    next: ok
      ? {
          status: `bun scripts/cli.ts hwlab nodes control-plane status --node ${options.node} --lane ${options.lane}`,
        }
      : { retry: `bun scripts/cli.ts hwlab nodes control-plane public-exposure --node ${options.node} --lane ${options.lane} --confirm` },
  };
}

function publicExposureSecretDryRunStatus(options: NodePublicExposureOptions, exposure: HwlabRuntimePk01PublicExposureSpec, tokenBytes: number): Record<string, unknown> {
  return {
    ok: true,
    dryRun: true,
    mutation: false,
    planned: true,
    namespace: options.spec.runtimeNamespace,
    secret: exposure.secretName,
    deployment: `${options.spec.runtimeNamespace}-frpc`,
    beforeExists: null,
    afterExists: null,
    tokenBytes,
    applyExitCode: null,
    restartMode: null,
    strategyPatchExitCode: null,
    rolloutRestartExitCode: null,
    scaleDownExitCode: null,
    scaleDownWaitExitCode: null,
    scaleDownWaitPodCount: null,
    scaleUpExitCode: null,
    rolloutStatusExitCode: null,
    readyReplicas: null,
    availableReplicas: null,
    desiredReplicas: null,
    strategyPatchErrorPreview: null,
    restartErrorPreview: null,
    scaleDownErrorPreview: null,
    scaleUpErrorPreview: null,
    readyErrorPreview: null,
    exitCode: 0,
    stderr: "",
    valuesRedacted: true,
  };
}

function publicExposureSecretApplyStatus(options: NodePublicExposureOptions, exposure: HwlabRuntimePk01PublicExposureSpec, token: string): Record<string, unknown> {
  const secretApplyResult = runTransScript(options.node, publicExposureSecretScript(options, exposure), token, options.timeoutSeconds);
  let secretFields = keyValueLinesFromText(statusText(secretApplyResult));
  let secretResult = secretApplyResult;
  if (secretApplyResult.exitCode === 0 && secretFields.afterSecretExists === "yes") {
    const restartResult = runPublicExposureFrpcRecreate(options);
    secretFields = { ...secretFields, ...keyValueLinesFromText(statusText(restartResult)) };
    secretResult = combinePublicExposureCommandResults(secretApplyResult, restartResult);
  }
  return publicExposureSecretStatus(secretFields, secretResult);
}

export function readPublicExposureTokenSource(exposure: HwlabRuntimePk01PublicExposureSpec): { ok: boolean; path: string; checkedPaths: string[]; key: string; value: string | null; fingerprint: string | null; error?: string } {
  const checkedPaths = publicExposureTokenSourcePaths(exposure);
  const path = checkedPaths.find((candidate) => existsSync(candidate)) ?? checkedPaths[0] ?? rootPath(".state", "secrets", exposure.tokenSourceRef);
  if (!existsSync(path)) return { ok: false, path, checkedPaths, key: exposure.tokenSourceKey, value: null, fingerprint: null, error: "secret-source-missing" };
  const values = parseEnvFile(readFileSync(path, "utf8"));
  const value = values[exposure.tokenSourceKey];
  if (value === undefined || value.length === 0) return { ok: false, path, checkedPaths, key: exposure.tokenSourceKey, value: null, fingerprint: null, error: "secret-key-missing" };
  return {
    ok: true,
    path,
    checkedPaths,
    key: exposure.tokenSourceKey,
    value,
    fingerprint: `sha256:${createHash("sha256").update(value).digest("hex").slice(0, 16)}`,
  };
}

export function publicExposureTokenSourcePaths(exposure: HwlabRuntimePk01PublicExposureSpec): string[] {
  return [rootPath(".state", "secrets", exposure.tokenSourceRef)];
}

export function publicExposureSecretStatus(fields: Record<string, string>, result: CommandResult): Record<string, unknown> {
  const dryRun = fields.dryRun === "true";
  return {
    ok: result.exitCode === 0 && (dryRun || (
      fields.afterSecretExists === "yes"
      && fields.strategyPatchExitCode === "0"
      && fields.rolloutRestartExitCode === "0"
      && fields.rolloutStatusExitCode === "0"
    )),
    dryRun,
    mutation: fields.mutation === "true",
    namespace: fields.namespace || null,
    secret: fields.secret || null,
    deployment: fields.deployment || null,
    beforeExists: fields.beforeSecretExists === "yes",
    afterExists: fields.afterSecretExists === "yes",
    tokenBytes: numericField(fields.tokenBytes),
    applyExitCode: numericField(fields.applyExitCode),
    restartMode: fields.restartMode || null,
    strategyPatchExitCode: numericField(fields.strategyPatchExitCode),
    rolloutRestartExitCode: numericField(fields.rolloutRestartExitCode),
    scaleDownExitCode: numericField(fields.scaleDownExitCode),
    scaleDownWaitExitCode: numericField(fields.scaleDownWaitExitCode),
    scaleDownWaitPodCount: numericField(fields.scaleDownWaitPodCount),
    scaleUpExitCode: numericField(fields.scaleUpExitCode),
    rolloutStatusExitCode: numericField(fields.rolloutStatusExitCode),
    readyReplicas: numericField(fields.readyReplicas),
    availableReplicas: numericField(fields.availableReplicas),
    desiredReplicas: numericField(fields.desiredReplicas),
    strategyPatchErrorPreview: fields.strategyPatchErrorPreview || null,
    restartErrorPreview: fields.restartErrorPreview || null,
    scaleDownErrorPreview: fields.scaleDownErrorPreview || null,
    scaleUpErrorPreview: fields.scaleUpErrorPreview || null,
    readyErrorPreview: fields.readyErrorPreview || null,
    exitCode: result.exitCode,
    stderr: result.exitCode === 0 ? "" : result.stderr.trim().slice(0, 2000),
  };
}

export function publicExposureCaddyStatus(fields: Record<string, string>, result: CommandResult): Record<string, unknown> {
  const dryRun = fields.dryRun === "true";
  return {
    ok: result.exitCode === 0
      && fields.afterBlockPresent === "yes"
      && fields.validateExitCode === "0"
      && (dryRun || (
        fields.active === "active"
        && fields.localWebStatus === "200"
        && fields.localApiStatus === "200"
      )),
    dryRun,
    mutation: fields.mutation === "true",
    hostname: fields.hostname || null,
    configPath: fields.configPath || null,
    beforeBlockPresent: fields.beforeBlockPresent === "yes",
    afterBlockPresent: fields.afterBlockPresent === "yes",
    staleBlocksRemoved: numericField(fields.staleBlocksRemoved),
    pythonExitCode: numericField(fields.pythonExitCode),
    validateMode: fields.validateMode || null,
    validateExitCode: numericField(fields.validateExitCode),
    reloadExitCode: numericField(fields.reloadExitCode),
    active: fields.active || null,
    localWebStatus: fields.localWebStatus || null,
    localApiStatus: fields.localApiStatus || null,
    validateErrorPreview: fields.validateErrorPreview || null,
    localWebErrorPreview: fields.localWebErrorPreview || null,
    localApiErrorPreview: fields.localApiErrorPreview || null,
    exitCode: result.exitCode,
    stderr: result.exitCode === 0 ? "" : result.stderr.trim().slice(0, 2000),
  };
}

export function publicExposureSecretScript(options: NodePublicExposureOptions, exposure: HwlabRuntimePk01PublicExposureSpec): string {
  const deployment = `${options.spec.runtimeNamespace}-frpc`;
  return [
    "set +e",
    `namespace=${shellQuote(options.spec.runtimeNamespace)}`,
    `secret=${shellQuote(exposure.secretName)}`,
    `deployment=${shellQuote(deployment)}`,
    `token_key=${shellQuote(exposure.tokenKey)}`,
    `dry_run=${shellQuote(options.dryRun ? "true" : "false")}`,
    "token=$(cat)",
    "before_secret_exists=no",
    "kubectl -n \"$namespace\" get secret \"$secret\" >/dev/null 2>&1 && before_secret_exists=yes",
    "token_bytes=$(printf '%s' \"$token\" | wc -c | tr -d ' ')",
    "apply_exit=",
    "restart_mode=recreate-rollout-restart",
    "strategy_patch_exit=",
    "rollout_restart_exit=",
    "scale_down_exit=",
    "scale_down_wait_exit=",
    "scale_down_wait_pod_count=",
    "scale_up_exit=",
    "rollout_status_exit=",
    "ready_replicas=",
    "available_replicas=",
    "desired_replicas=",
    "mutation=false",
    "if [ \"$dry_run\" = false ]; then",
    "  tmp=$(mktemp /tmp/hwlab-public-frpc-secret.XXXXXX.yaml)",
    "  token_b64=$(printf '%s' \"$token\" | base64 | tr -d '\\n')",
    "  cat >\"$tmp\" <<EOF",
    "apiVersion: v1",
    "kind: Secret",
    "metadata:",
    "  name: $secret",
    "  namespace: $namespace",
    "  labels:",
    "    app.kubernetes.io/part-of: hwlab",
    "    app.kubernetes.io/managed-by: unidesk",
    "type: Opaque",
    "data:",
    "  $token_key: $token_b64",
    "EOF",
    "  kubectl apply --server-side --force-conflicts --field-manager=unidesk-hwlab-node-public-exposure -f \"$tmp\" >/tmp/hwlab-public-frpc-secret.apply.out 2>/tmp/hwlab-public-frpc-secret.apply.err",
    "  apply_exit=$?",
    "  rm -f \"$tmp\"",
    "  if [ \"$apply_exit\" = 0 ]; then mutation=true; fi",
    "fi",
    "after_secret_exists=no",
    "kubectl -n \"$namespace\" get secret \"$secret\" >/dev/null 2>&1 && after_secret_exists=yes",
    "printf 'namespace\\t%s\\n' \"$namespace\"",
    "printf 'secret\\t%s\\n' \"$secret\"",
    "printf 'deployment\\t%s\\n' \"$deployment\"",
    "printf 'dryRun\\t%s\\n' \"$dry_run\"",
    "printf 'mutation\\t%s\\n' \"$mutation\"",
    "printf 'beforeSecretExists\\t%s\\n' \"$before_secret_exists\"",
    "printf 'afterSecretExists\\t%s\\n' \"$after_secret_exists\"",
    "printf 'tokenBytes\\t%s\\n' \"$token_bytes\"",
    "printf 'applyExitCode\\t%s\\n' \"$apply_exit\"",
    "printf 'restartMode\\t%s\\n' \"$restart_mode\"",
    "printf 'strategyPatchExitCode\\t%s\\n' \"$strategy_patch_exit\"",
    "printf 'rolloutRestartExitCode\\t%s\\n' \"$rollout_restart_exit\"",
    "printf 'scaleDownExitCode\\t%s\\n' \"$scale_down_exit\"",
    "printf 'scaleDownWaitExitCode\\t%s\\n' \"$scale_down_wait_exit\"",
    "printf 'scaleDownWaitPodCount\\t%s\\n' \"$scale_down_wait_pod_count\"",
    "printf 'scaleUpExitCode\\t%s\\n' \"$scale_up_exit\"",
    "printf 'rolloutStatusExitCode\\t%s\\n' \"$rollout_status_exit\"",
    "printf 'readyReplicas\\t%s\\n' \"$ready_replicas\"",
    "printf 'availableReplicas\\t%s\\n' \"$available_replicas\"",
    "printf 'desiredReplicas\\t%s\\n' \"$desired_replicas\"",
    "if [ \"$dry_run\" = true ]; then exit 0; fi",
    "[ \"$after_secret_exists\" = yes ] && [ \"$apply_exit\" = 0 ]",
  ].join("\n");
}

export function runPublicExposureFrpcRecreate(options: NodePublicExposureOptions): CommandResult {
  const namespace = options.spec.runtimeNamespace;
  const deployment = `${namespace}-frpc`;
  const fields: Record<string, string> = {
    deployment,
    restartMode: "recreate-rollout-restart",
    strategyPatchExitCode: "",
    rolloutRestartExitCode: "",
    scaleDownExitCode: "",
    scaleDownWaitExitCode: "",
    scaleDownWaitPodCount: "",
    scaleUpExitCode: "",
    rolloutStatusExitCode: "",
    readyReplicas: "",
    availableReplicas: "",
    desiredReplicas: "",
    strategyPatchErrorPreview: "",
    restartErrorPreview: "",
    scaleDownErrorPreview: "",
    scaleUpErrorPreview: "",
    readyErrorPreview: "",
  };
  const stdoutParts: string[] = [];
  const stderrParts: string[] = [];
  const addResult = (result: CommandResult): void => {
    if (result.stdout.trim()) stdoutParts.push(result.stdout.trim());
    if (result.stderr.trim()) stderrParts.push(result.stderr.trim());
  };
  const shortTimeout = Math.min(Math.max(20, options.timeoutSeconds), 55);
  const strategyPatch = runTransScript(options.node, [
    "set +e",
    `namespace=${shellQuote(namespace)}`,
    `deployment=${shellQuote(deployment)}`,
    "kubectl -n \"$namespace\" patch deploy/\"$deployment\" --type=merge -p '{\"spec\":{\"strategy\":{\"type\":\"Recreate\",\"rollingUpdate\":null}}}' >/tmp/hwlab-public-frpc-strategy-patch.out 2>/tmp/hwlab-public-frpc-strategy-patch.err",
    "code=$?",
    "printf 'strategyPatchExitCode\\t%s\\n' \"$code\"",
    "printf 'strategyPatchErrorPreview\\t%s\\n' \"$([ -f /tmp/hwlab-public-frpc-strategy-patch.err ] && tr '\\n' ';' </tmp/hwlab-public-frpc-strategy-patch.err | cut -c1-1000 || true)\"",
    "exit \"$code\"",
  ].join("\n"), "", shortTimeout);
  addResult(strategyPatch);
  Object.assign(fields, keyValueLinesFromText(statusText(strategyPatch)));

  if (strategyPatch.exitCode === 0) {
    const restart = runTransScript(options.node, [
      "set +e",
      `namespace=${shellQuote(namespace)}`,
      `deployment=${shellQuote(deployment)}`,
      "kubectl -n \"$namespace\" rollout restart deploy/\"$deployment\" >/tmp/hwlab-public-frpc-restart.out 2>/tmp/hwlab-public-frpc-restart.err",
      "code=$?",
      "printf 'rolloutRestartExitCode\\t%s\\n' \"$code\"",
      "printf 'restartErrorPreview\\t%s\\n' \"$([ -f /tmp/hwlab-public-frpc-restart.err ] && tr '\\n' ';' </tmp/hwlab-public-frpc-restart.err | cut -c1-1000 || true)\"",
      "exit \"$code\"",
    ].join("\n"), "", shortTimeout);
    addResult(restart);
    Object.assign(fields, keyValueLinesFromText(statusText(restart)));
  }

  if (fields.rolloutRestartExitCode === "0") {
    const deadline = Date.now() + 55_000;
    while (Date.now() <= deadline) {
      const probe = runTransScript(options.node, [
        "set +e",
        `namespace=${shellQuote(namespace)}`,
        `deployment=${shellQuote(deployment)}`,
        "kubectl -n \"$namespace\" get deploy \"$deployment\" -o jsonpath='{.spec.replicas}{\"\\t\"}{.status.readyReplicas}{\"\\t\"}{.status.availableReplicas}' >/tmp/hwlab-public-frpc-ready.out 2>/tmp/hwlab-public-frpc-ready.err",
        "code=$?",
        "values=$(cat /tmp/hwlab-public-frpc-ready.out 2>/dev/null || true)",
        "desired=$(printf '%s' \"$values\" | cut -f1)",
        "ready=$(printf '%s' \"$values\" | cut -f2)",
        "available=$(printf '%s' \"$values\" | cut -f3)",
        "printf 'desiredReplicas\\t%s\\n' \"$desired\"",
        "printf 'readyReplicas\\t%s\\n' \"$ready\"",
        "printf 'availableReplicas\\t%s\\n' \"$available\"",
        "printf 'readyErrorPreview\\t%s\\n' \"$([ -f /tmp/hwlab-public-frpc-ready.err ] && tr '\\n' ';' </tmp/hwlab-public-frpc-ready.err | cut -c1-1000 || true)\"",
        "[ \"$code\" = 0 ] && [ \"$desired\" = 1 ] && [ \"$ready\" = 1 ] && [ \"$available\" = 1 ]",
      ].join("\n"), "", shortTimeout);
      addResult(probe);
      Object.assign(fields, keyValueLinesFromText(statusText(probe)));
      if (probe.exitCode === 0) {
        fields.rolloutStatusExitCode = "0";
        break;
      }
      runCommand(["sleep", "2"], repoRoot, { timeoutMs: 3_000 });
    }
  }
  if (fields.rolloutStatusExitCode === "") fields.rolloutStatusExitCode = "1";

  const ok = fields.strategyPatchExitCode === "0"
    && fields.rolloutRestartExitCode === "0"
    && fields.rolloutStatusExitCode === "0";
  const stdout = Object.entries(fields).map(([key, value]) => `${key}\t${value}`).join("\n") + "\n";
  return {
    command: [transPath(), `${options.node}:k3s`, "sh", "--", "<public-exposure-frpc-recreate>"],
    cwd: repoRoot,
    exitCode: ok ? 0 : 1,
    stdout: [stdout.trim(), ...stdoutParts].filter(Boolean).join("\n") + "\n",
    stderr: stderrParts.join("\n"),
    signal: null,
    timedOut: false,
  };
}

export function combinePublicExposureCommandResults(first: CommandResult, second: CommandResult): CommandResult {
  return {
    command: [...first.command, "&&", ...second.command],
    cwd: repoRoot,
    exitCode: first.exitCode === 0 && second.exitCode === 0 ? 0 : second.exitCode ?? first.exitCode,
    stdout: [first.stdout.trim(), second.stdout.trim()].filter(Boolean).join("\n") + "\n",
    stderr: [first.stderr.trim(), second.stderr.trim()].filter(Boolean).join("\n"),
    signal: second.signal ?? first.signal,
    timedOut: first.timedOut || second.timedOut,
  };
}

export function publicExposureCaddyScript(options: NodePublicExposureOptions, exposure: HwlabRuntimePk01PublicExposureSpec): string {
  const blockB64 = Buffer.from(publicExposureCaddyBlock(exposure), "utf8").toString("base64");
  const marker = `unidesk managed ${exposure.hostname}`;
  return [
    "set +e",
    `dry_run=${shellQuote(options.dryRun ? "true" : "false")}`,
    `hostname=${shellQuote(exposure.hostname)}`,
    `config_path=${shellQuote(exposure.caddyConfigPath)}`,
    `service=${shellQuote(exposure.caddyServiceName)}`,
    `marker=${shellQuote(marker)}`,
    `block_b64=${shellQuote(blockB64)}`,
    `expected_a=${shellQuote(exposure.expectedA)}`,
    `web_port=${shellQuote(String(exposure.webProxy.remotePort))}`,
    `api_port=${shellQuote(String(exposure.apiProxy.remotePort))}`,
    "rm -f /tmp/hwlab-public-caddy-validate.out /tmp/hwlab-public-caddy-validate.err /tmp/hwlab-public-caddy-python.err /tmp/hwlab-public-caddy-web.err /tmp/hwlab-public-caddy-api.err",
    "tmp=$(mktemp -d)",
    "block=\"$tmp/block\"",
    "next=\"$tmp/Caddyfile\"",
    "printf '%s' \"$block_b64\" | base64 -d >\"$block\"",
    "before_present=no",
    "if [ -f \"$config_path\" ] && grep -Fq \"# BEGIN $marker\" \"$config_path\"; then before_present=yes; fi",
    "if [ -f \"$config_path\" ]; then cp \"$config_path\" \"$next\"; else : >\"$next\"; fi",
    "stale_blocks_removed=$(python3 - \"$next\" \"$block\" \"$marker\" \"$web_port\" \"$api_port\" 2>/tmp/hwlab-public-caddy-python.err <<'PY'",
    "import pathlib, re, sys",
    "config = pathlib.Path(sys.argv[1])",
    "block = pathlib.Path(sys.argv[2]).read_text(encoding='utf-8')",
    "marker = sys.argv[3]",
    "web_port = sys.argv[4]",
    "api_port = sys.argv[5]",
    "current_name = marker[len('unidesk managed '):] if marker.startswith('unidesk managed ') else marker",
    "text = config.read_text(encoding='utf-8') if config.exists() else ''",
    "begin = f'# BEGIN {marker}'",
    "end = f'# END {marker}'",
    "managed = f'{begin}\\n{block.rstrip()}\\n{end}\\n'",
    "stale_removed = [0]",
    "pattern = re.compile(r'(?ms)^# BEGIN unidesk managed (?P<name>[^\\n]+)\\n(?P<body>.*?)\\n# END unidesk managed (?P=name)\\n*')",
    "def keep(match):",
    "    name = match.group('name')",
    "    body = match.group('body')",
    "    if name != current_name and (f'127.0.0.1:{web_port}' in body or f'127.0.0.1:{api_port}' in body):",
    "        stale_removed[0] += 1",
    "        return ''",
    "    return match.group(0)",
    "text = pattern.sub(keep, text)",
    "if begin in text and end in text:",
    "    start = text.index(begin)",
    "    stop = text.index(end, start) + len(end)",
    "    text = text[:start].rstrip() + '\\n\\n' + managed.rstrip() + '\\n' + text[stop:].lstrip('\\n')",
    "else:",
    "    text = text.rstrip() + '\\n\\n' + managed",
    "config.write_text(text, encoding='utf-8')",
    "print(stale_removed[0])",
    "PY",
    ")",
    "python_exit=$?",
    "validate_exit=1",
    "validate_mode=validate",
    "if [ \"$python_exit\" != 0 ]; then",
    "  validate_mode=python",
    "  validate_exit=$python_exit",
    "else",
    "  if [ \"$dry_run\" = true ]; then",
    "    validate_mode=adapt",
    "    caddy adapt --config \"$next\" --adapter caddyfile >/tmp/hwlab-public-caddy-validate.out 2>/tmp/hwlab-public-caddy-validate.err",
    "  else",
    "    sudo caddy validate --config \"$next\" --adapter caddyfile >/tmp/hwlab-public-caddy-validate.out 2>/tmp/hwlab-public-caddy-validate.err",
    "  fi",
    "  validate_exit=$?",
    "fi",
    "reload_exit=",
    "mutation=false",
    "if [ \"$dry_run\" = false ] && [ \"$validate_exit\" = 0 ]; then",
    "  sudo install -m 0644 \"$next\" \"$config_path\" >/tmp/hwlab-public-caddy-install.out 2>/tmp/hwlab-public-caddy-install.err",
    "  install_exit=$?",
    "  if [ \"$install_exit\" = 0 ]; then",
    "    mutation=true",
    "    sudo systemctl reload \"$service\" >/tmp/hwlab-public-caddy-reload.out 2>/tmp/hwlab-public-caddy-reload.err || sudo systemctl restart \"$service\" >>/tmp/hwlab-public-caddy-reload.out 2>>/tmp/hwlab-public-caddy-reload.err",
    "    reload_exit=$?",
    "  else",
    "    reload_exit=$install_exit",
    "  fi",
    "fi",
    "active=$(systemctl is-active \"$service\" 2>/dev/null || true)",
    "after_present=no",
    "if [ \"$dry_run\" = true ]; then grep -Fq \"# BEGIN $marker\" \"$next\" && after_present=yes; else grep -Fq \"# BEGIN $marker\" \"$config_path\" && after_present=yes; fi",
    "local_web_status=",
    "local_api_status=",
    "if [ \"$dry_run\" = false ]; then",
    "  local_web_status=$(curl -kfsS --max-time 15 --resolve \"$hostname:443:127.0.0.1\" \"https://$hostname/\" -o /dev/null -w '%{http_code}' 2>/tmp/hwlab-public-caddy-web.err || true)",
    "  local_api_status=$(curl -kfsS --max-time 15 --resolve \"$hostname:443:127.0.0.1\" \"https://$hostname/health/live\" -o /dev/null -w '%{http_code}' 2>/tmp/hwlab-public-caddy-api.err || true)",
    "fi",
    "validate_err_preview=$(cat /tmp/hwlab-public-caddy-python.err /tmp/hwlab-public-caddy-validate.err 2>/dev/null | tr '\\n' ';' | cut -c1-1000 || true)",
    "local_web_err_preview=$([ -f /tmp/hwlab-public-caddy-web.err ] && tr '\\n' ';' </tmp/hwlab-public-caddy-web.err 2>/dev/null | cut -c1-1000 || true)",
    "local_api_err_preview=$([ -f /tmp/hwlab-public-caddy-api.err ] && tr '\\n' ';' </tmp/hwlab-public-caddy-api.err 2>/dev/null | cut -c1-1000 || true)",
    "printf 'hostname\\t%s\\n' \"$hostname\"",
    "printf 'expectedA\\t%s\\n' \"$expected_a\"",
    "printf 'configPath\\t%s\\n' \"$config_path\"",
    "printf 'dryRun\\t%s\\n' \"$dry_run\"",
    "printf 'mutation\\t%s\\n' \"$mutation\"",
    "printf 'beforeBlockPresent\\t%s\\n' \"$before_present\"",
    "printf 'afterBlockPresent\\t%s\\n' \"$after_present\"",
    "printf 'staleBlocksRemoved\\t%s\\n' \"$stale_blocks_removed\"",
    "printf 'pythonExitCode\\t%s\\n' \"$python_exit\"",
    "printf 'validateMode\\t%s\\n' \"$validate_mode\"",
    "printf 'validateExitCode\\t%s\\n' \"$validate_exit\"",
    "printf 'reloadExitCode\\t%s\\n' \"$reload_exit\"",
    "printf 'active\\t%s\\n' \"$active\"",
    "printf 'localWebStatus\\t%s\\n' \"$local_web_status\"",
    "printf 'localApiStatus\\t%s\\n' \"$local_api_status\"",
    "printf 'validateErrorPreview\\t%s\\n' \"$validate_err_preview\"",
    "printf 'localWebErrorPreview\\t%s\\n' \"$local_web_err_preview\"",
    "printf 'localApiErrorPreview\\t%s\\n' \"$local_api_err_preview\"",
    "rm -rf \"$tmp\"",
    "[ \"$validate_exit\" = 0 ] && [ \"$after_present\" = yes ]",
  ].join("\n");
}

export function publicExposureCaddyBlock(exposure: HwlabRuntimePk01PublicExposureSpec): string {
  const tlsLines = exposure.caddyTls === "internal" ? "    tls internal\n" : "";
  const mainBlock = `${exposure.hostname} {
${tlsLines}    @api path /health* /v1* /json-rpc* /openapi* /docs* /swagger*
    handle @api {
        reverse_proxy 127.0.0.1:${exposure.apiProxy.remotePort} {
            transport http {
                response_header_timeout ${exposure.responseHeaderTimeoutSeconds}s
            }
        }
    }
    handle {
        reverse_proxy 127.0.0.1:${exposure.webProxy.remotePort} {
            transport http {
                response_header_timeout ${exposure.responseHeaderTimeoutSeconds}s
            }
        }
    }
}`;
  const extraBlocks = exposure.extraProxies
    .filter((proxy) => proxy.hostname !== undefined)
    .map((proxy) => `${proxy.hostname} {
${tlsLines}    reverse_proxy 127.0.0.1:${proxy.remotePort} {
        transport http {
            response_header_timeout ${exposure.responseHeaderTimeoutSeconds}s
        }
    }
}`);
  return [mainBlock, ...extraBlocks].join("\n\n");
}

export function runTransScript(node: string, script: string, input: string, timeoutSeconds: number): CommandResult {
  return runCommand([transPath(), `${node}:k3s`, "sh", "--", script], repoRoot, { input, timeoutMs: timeoutSeconds * 1000 });
}

export function runTransHostScript(node: string, script: string, input: string, timeoutSeconds: number): CommandResult {
  return runCommand([transPath(), node, "sh", "--", script], repoRoot, { input, timeoutMs: timeoutSeconds * 1000 });
}

export function runTransWorkspaceStdinScript(node: string, workspace: string, script: string, timeoutSeconds: number): CommandResult {
  return runCommand([transPath(), `${node}:${workspace}`, "sh"], repoRoot, { input: script, timeoutMs: timeoutSeconds * 1000 });
}

export function runObsoleteSecretCleanup(options: NodeSecretOptions, spec: RuntimeSecretSpec): Record<string, unknown> {
  const kubernetesResult = runTransScript(options.node, obsoleteSecretCleanupScript(options, spec), "", options.timeoutSeconds);
  const kubernetesStatus = secretStatusFromText(statusText(kubernetesResult), kubernetesResult.exitCode === 0, kubernetesResult.exitCode, kubernetesResult.stderr, spec);
  const hostOptions = { ...options, dryRun: options.dryRun || kubernetesStatus.ok !== true };
  const platformDbResult = runTransHostScript(options.node, obsoletePlatformDbCleanupScript(hostOptions, spec), "", options.timeoutSeconds);
  const platformDbStatus = obsoletePlatformDbStatusFromText(statusText(platformDbResult), platformDbResult.exitCode === 0, platformDbResult.exitCode, platformDbResult.stderr, spec);
  const ok = kubernetesStatus.ok === true && platformDbStatus.ok === true && (options.dryRun || hostOptions.dryRun === false);
  const mutation = kubernetesStatus.mutation === true || platformDbStatus.mutation === true;
  return {
    ok,
    command: `hwlab nodes secret ${options.action}`,
    node: options.node,
    lane: options.lane,
    namespace: spec.namespace,
    secret: options.name,
    key: null,
    preset: options.preset,
    mode: options.dryRun ? "dry-run" : "confirmed-delete",
    status: {
      ok,
      preset: "obsolete-hwpod-db-cleanup",
      dryRun: options.dryRun,
      mutation,
      kubernetesSecret: kubernetesStatus,
      platformDatabase: platformDbStatus,
      hostMutationSkipped: !options.dryRun && hostOptions.dryRun,
      summary: ok
        ? `${spec.obsoleteHwpodDbSecret}, ${spec.obsoleteHwpodDbName}, and ${spec.obsoleteHwpodDbUser} are absent or ready to remove`
        : `${spec.obsoleteHwpodDbSecret}, ${spec.obsoleteHwpodDbName}, or ${spec.obsoleteHwpodDbUser} still needs cleanup`,
    },
    mutation,
    result: {
      kubernetesSecret: compactCommandResult(kubernetesResult),
      platformDatabase: compactCommandResult(platformDbResult),
    },
    valuesRedacted: true,
    next: ok ? undefined : nextSecretCommand(options, spec),
  };
}
