File size: 2,346 Bytes
f778c12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import { readConfigFileSnapshot } from "../../config/config.js";
import { resolveFutureConfigActionBlock } from "../../config/future-version-guard.js";
import { renderConfigValidationIssueLines } from "../../config/issue-location.js";
import { isPluginPackagingRuntimeOutputInvalidConfigSnapshot } from "../../config/recovery-policy.js";
import type { ConfigFileSnapshot } from "../../config/types.openclaw.js";
import { formatPluginPackagingRuntimeOutputRecoveryHint } from "../config-recovery-hints.js";

/** Service lifecycle actions; only start/restart bring the gateway up. */
type DaemonServiceAction = "start" | "restart" | "stop" | "uninstall";

type ServiceActionPreflightFailure = {
  message: string;
  hints?: string[];
};

const ACTION_PROSE: Record<DaemonServiceAction, string> = {
  start: "start the gateway service",
  restart: "restart the gateway service",
  stop: "stop the gateway service",
  uninstall: "uninstall the gateway service",
};

function formatPluginPackagingRuntimeOutputRecoveryHints(): string[] {
  return formatPluginPackagingRuntimeOutputRecoveryHint().split("\n");
}

/** Best-effort validation before a service action mutates runtime state. */
export async function getServiceActionPreflightFailure(
  action: DaemonServiceAction,
): Promise<ServiceActionPreflightFailure | null> {
  let snapshot: ConfigFileSnapshot;
  try {
    // Stop must remain available before Doctor migrates newly installed plugins.
    // Core validation and the newer-writer guard still protect service selection.
    snapshot = await readConfigFileSnapshot({
      observe: false,
      pluginValidation: action === "stop" ? "core-only" : undefined,
    });
    if (snapshot.exists && !snapshot.valid) {
      const message =
        snapshot.issues.length > 0
          ? renderConfigValidationIssueLines(snapshot, "").join("\n")
          : "Unknown validation issue.";
      return {
        message,
        ...(isPluginPackagingRuntimeOutputInvalidConfigSnapshot(snapshot)
          ? { hints: formatPluginPackagingRuntimeOutputRecoveryHints() }
          : {}),
      };
    }
  } catch {
    return null;
  }

  const futureBlock = resolveFutureConfigActionBlock({ action: ACTION_PROSE[action], snapshot });
  if (futureBlock) {
    return { message: futureBlock.message, hints: futureBlock.hints };
  }
  return null;
}