mirror of https://github.com/openclaw/openclaw.git
refactor: deduplicate acpx availability checks
This commit is contained in:
parent
202765c810
commit
f4ed317083
|
|
@ -13,7 +13,7 @@ import type {
|
||||||
} from "openclaw/plugin-sdk/acpx";
|
} from "openclaw/plugin-sdk/acpx";
|
||||||
import { AcpRuntimeError } from "openclaw/plugin-sdk/acpx";
|
import { AcpRuntimeError } from "openclaw/plugin-sdk/acpx";
|
||||||
import { toAcpMcpServers, type ResolvedAcpxPluginConfig } from "./config.js";
|
import { toAcpMcpServers, type ResolvedAcpxPluginConfig } from "./config.js";
|
||||||
import { checkAcpxVersion } from "./ensure.js";
|
import { checkAcpxVersion, type AcpxVersionCheckResult } from "./ensure.js";
|
||||||
import {
|
import {
|
||||||
parseJsonLines,
|
parseJsonLines,
|
||||||
parsePromptEventLine,
|
parsePromptEventLine,
|
||||||
|
|
@ -51,6 +51,28 @@ const ACPX_CAPABILITIES: AcpRuntimeCapabilities = {
|
||||||
controls: ["session/set_mode", "session/set_config_option", "session/status"],
|
controls: ["session/set_mode", "session/set_config_option", "session/status"],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type AcpxHealthCheckResult =
|
||||||
|
| {
|
||||||
|
ok: true;
|
||||||
|
versionCheck: Extract<AcpxVersionCheckResult, { ok: true }>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
ok: false;
|
||||||
|
failure:
|
||||||
|
| {
|
||||||
|
kind: "version-check";
|
||||||
|
versionCheck: Extract<AcpxVersionCheckResult, { ok: false }>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: "help-check";
|
||||||
|
result: Awaited<ReturnType<typeof spawnAndCollect>>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: "exception";
|
||||||
|
error: unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
function formatPermissionModeGuidance(): string {
|
function formatPermissionModeGuidance(): string {
|
||||||
return "Configure plugins.entries.acpx.config.permissionMode to one of: approve-reads, approve-all, deny-all.";
|
return "Configure plugins.entries.acpx.config.permissionMode to one of: approve-reads, approve-all, deny-all.";
|
||||||
}
|
}
|
||||||
|
|
@ -165,35 +187,71 @@ export class AcpxRuntime implements AcpRuntime {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async probeAvailability(): Promise<void> {
|
private async checkVersion(): Promise<AcpxVersionCheckResult> {
|
||||||
const versionCheck = await checkAcpxVersion({
|
return await checkAcpxVersion({
|
||||||
command: this.config.command,
|
command: this.config.command,
|
||||||
cwd: this.config.cwd,
|
cwd: this.config.cwd,
|
||||||
expectedVersion: this.config.expectedVersion,
|
expectedVersion: this.config.expectedVersion,
|
||||||
stripProviderAuthEnvVars: this.config.stripProviderAuthEnvVars,
|
stripProviderAuthEnvVars: this.config.stripProviderAuthEnvVars,
|
||||||
spawnOptions: this.spawnCommandOptions,
|
spawnOptions: this.spawnCommandOptions,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async runHelpCheck(): Promise<Awaited<ReturnType<typeof spawnAndCollect>>> {
|
||||||
|
return await spawnAndCollect(
|
||||||
|
{
|
||||||
|
command: this.config.command,
|
||||||
|
args: ["--help"],
|
||||||
|
cwd: this.config.cwd,
|
||||||
|
stripProviderAuthEnvVars: this.config.stripProviderAuthEnvVars,
|
||||||
|
},
|
||||||
|
this.spawnCommandOptions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async checkHealth(): Promise<AcpxHealthCheckResult> {
|
||||||
|
const versionCheck = await this.checkVersion();
|
||||||
if (!versionCheck.ok) {
|
if (!versionCheck.ok) {
|
||||||
this.healthy = false;
|
return {
|
||||||
return;
|
ok: false,
|
||||||
|
failure: {
|
||||||
|
kind: "version-check",
|
||||||
|
versionCheck,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await spawnAndCollect(
|
const result = await this.runHelpCheck();
|
||||||
{
|
if (result.error != null || (result.code ?? 0) !== 0) {
|
||||||
command: this.config.command,
|
return {
|
||||||
args: ["--help"],
|
ok: false,
|
||||||
cwd: this.config.cwd,
|
failure: {
|
||||||
stripProviderAuthEnvVars: this.config.stripProviderAuthEnvVars,
|
kind: "help-check",
|
||||||
|
result,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
versionCheck,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
failure: {
|
||||||
|
kind: "exception",
|
||||||
|
error,
|
||||||
},
|
},
|
||||||
this.spawnCommandOptions,
|
};
|
||||||
);
|
|
||||||
this.healthy = result.error == null && (result.code ?? 0) === 0;
|
|
||||||
} catch {
|
|
||||||
this.healthy = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async probeAvailability(): Promise<void> {
|
||||||
|
const result = await this.checkHealth();
|
||||||
|
this.healthy = result.ok;
|
||||||
|
}
|
||||||
|
|
||||||
async ensureSession(input: AcpRuntimeEnsureInput): Promise<AcpRuntimeHandle> {
|
async ensureSession(input: AcpRuntimeEnsureInput): Promise<AcpRuntimeHandle> {
|
||||||
const sessionName = asTrimmedString(input.sessionKey);
|
const sessionName = asTrimmedString(input.sessionKey);
|
||||||
if (!sessionName) {
|
if (!sessionName) {
|
||||||
|
|
@ -494,14 +552,9 @@ export class AcpxRuntime implements AcpRuntime {
|
||||||
}
|
}
|
||||||
|
|
||||||
async doctor(): Promise<AcpRuntimeDoctorReport> {
|
async doctor(): Promise<AcpRuntimeDoctorReport> {
|
||||||
const versionCheck = await checkAcpxVersion({
|
const result = await this.checkHealth();
|
||||||
command: this.config.command,
|
if (!result.ok && result.failure.kind === "version-check") {
|
||||||
cwd: this.config.cwd,
|
const { versionCheck } = result.failure;
|
||||||
expectedVersion: this.config.expectedVersion,
|
|
||||||
stripProviderAuthEnvVars: this.config.stripProviderAuthEnvVars,
|
|
||||||
spawnOptions: this.spawnCommandOptions,
|
|
||||||
});
|
|
||||||
if (!versionCheck.ok) {
|
|
||||||
this.healthy = false;
|
this.healthy = false;
|
||||||
const details = [
|
const details = [
|
||||||
versionCheck.expectedVersion ? `expected=${versionCheck.expectedVersion}` : null,
|
versionCheck.expectedVersion ? `expected=${versionCheck.expectedVersion}` : null,
|
||||||
|
|
@ -516,20 +569,12 @@ export class AcpxRuntime implements AcpRuntime {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (!result.ok && result.failure.kind === "help-check") {
|
||||||
const result = await spawnAndCollect(
|
const { result: helpResult } = result.failure;
|
||||||
{
|
this.healthy = false;
|
||||||
command: this.config.command,
|
if (helpResult.error) {
|
||||||
args: ["--help"],
|
const spawnFailure = resolveSpawnFailure(helpResult.error, this.config.cwd);
|
||||||
cwd: this.config.cwd,
|
|
||||||
stripProviderAuthEnvVars: this.config.stripProviderAuthEnvVars,
|
|
||||||
},
|
|
||||||
this.spawnCommandOptions,
|
|
||||||
);
|
|
||||||
if (result.error) {
|
|
||||||
const spawnFailure = resolveSpawnFailure(result.error, this.config.cwd);
|
|
||||||
if (spawnFailure === "missing-command") {
|
if (spawnFailure === "missing-command") {
|
||||||
this.healthy = false;
|
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
code: "ACP_BACKEND_UNAVAILABLE",
|
code: "ACP_BACKEND_UNAVAILABLE",
|
||||||
|
|
@ -538,42 +583,44 @@ export class AcpxRuntime implements AcpRuntime {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (spawnFailure === "missing-cwd") {
|
if (spawnFailure === "missing-cwd") {
|
||||||
this.healthy = false;
|
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
code: "ACP_BACKEND_UNAVAILABLE",
|
code: "ACP_BACKEND_UNAVAILABLE",
|
||||||
message: `ACP runtime working directory does not exist: ${this.config.cwd}`,
|
message: `ACP runtime working directory does not exist: ${this.config.cwd}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
this.healthy = false;
|
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
code: "ACP_BACKEND_UNAVAILABLE",
|
code: "ACP_BACKEND_UNAVAILABLE",
|
||||||
message: result.error.message,
|
message: helpResult.error.message,
|
||||||
details: [String(result.error)],
|
details: [String(helpResult.error)],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if ((result.code ?? 0) !== 0) {
|
|
||||||
this.healthy = false;
|
|
||||||
return {
|
|
||||||
ok: false,
|
|
||||||
code: "ACP_BACKEND_UNAVAILABLE",
|
|
||||||
message: result.stderr.trim() || `acpx exited with code ${result.code ?? "unknown"}`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
this.healthy = true;
|
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: false,
|
||||||
message: `acpx command available (${this.config.command}, version ${versionCheck.version}${this.config.expectedVersion ? `, expected ${this.config.expectedVersion}` : ""})`,
|
code: "ACP_BACKEND_UNAVAILABLE",
|
||||||
|
message:
|
||||||
|
helpResult.stderr.trim() || `acpx exited with code ${helpResult.code ?? "unknown"}`,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
}
|
||||||
|
|
||||||
|
if (!result.ok) {
|
||||||
this.healthy = false;
|
this.healthy = false;
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
code: "ACP_BACKEND_UNAVAILABLE",
|
code: "ACP_BACKEND_UNAVAILABLE",
|
||||||
message: error instanceof Error ? error.message : String(error),
|
message:
|
||||||
|
result.failure.error instanceof Error
|
||||||
|
? result.failure.error.message
|
||||||
|
: String(result.failure.error),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.healthy = true;
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
message: `acpx command available (${this.config.command}, version ${result.versionCheck.version}${this.config.expectedVersion ? `, expected ${this.config.expectedVersion}` : ""})`,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async cancel(input: { handle: AcpRuntimeHandle; reason?: string }): Promise<void> {
|
async cancel(input: { handle: AcpRuntimeHandle; reason?: string }): Promise<void> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue