mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix(daemon): handle systemctl is-enabled exit 4 (not-found) on Ubuntu (#33634)
Merged via squash.
Prepared head SHA: 67dffc3ee2
Co-authored-by: Yuandiaodiaodiao <33371662+Yuandiaodiaodiao@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd
This commit is contained in:
@@ -17,6 +17,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Models/custom provider headers: propagate `models.providers.<name>.headers` across inline, fallback, and registry-found model resolution so header-authenticated proxies consistently receive configured request headers. (#27490) thanks @Sid-Qin.
|
||||
- Daemon/systemd install robustness: treat `systemctl --user is-enabled` exit-code-4 `not-found` responses as not-enabled by combining stderr/stdout detail parsing, so Ubuntu fresh installs no longer fail with `systemctl is-enabled unavailable`. (#33634) Thanks @Yuandiaodiaodiao.
|
||||
- Slack/system-event session routing: resolve reaction/member/pin/interaction system-event session keys through channel/account bindings (with sender-aware DM routing) so inbound Slack events target the correct agent session in multi-account setups instead of defaulting to `agent:main`. (#34045) Thanks @paulomcg, @daht-mad and @vincentkoc.
|
||||
- Gateway/HTTP tools invoke media compatibility: preserve raw media payload access for direct `/tools/invoke` clients by allowing media `nodes` invoke commands only in HTTP tool context, while keeping agent-context media invoke blocking to prevent base64 prompt bloat. (#34365) Thanks @obviyus.
|
||||
- Agents/Nodes media outputs: add dedicated `photos_latest` action handling, block media-returning `nodes invoke` commands, keep metadata-only `camera.list` invoke allowed, and normalize empty `photos_latest` results to a consistent response shape to prevent base64 context bloat. (#34332) Thanks @obviyus.
|
||||
|
||||
@@ -90,6 +90,21 @@ describe("isSystemdServiceEnabled", () => {
|
||||
"systemctl is-enabled unavailable: Failed to connect to bus",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns false when systemctl is-enabled exits with code 4 (not-found)", async () => {
|
||||
const { isSystemdServiceEnabled } = await import("./systemd.js");
|
||||
execFileMock.mockImplementationOnce((_cmd, _args, _opts, cb) => {
|
||||
// On Ubuntu 24.04, `systemctl --user is-enabled <unit>` exits with
|
||||
// code 4 and prints "not-found" to stdout when the unit doesn't exist.
|
||||
const err = new Error(
|
||||
"Command failed: systemctl --user is-enabled openclaw-gateway.service",
|
||||
) as Error & { code?: number };
|
||||
err.code = 4;
|
||||
cb(err, "not-found\n", "");
|
||||
});
|
||||
const result = await isSystemdServiceEnabled({ env: {} });
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("systemd runtime parsing", () => {
|
||||
|
||||
@@ -143,7 +143,10 @@ async function execSystemctl(
|
||||
}
|
||||
|
||||
function readSystemctlDetail(result: { stdout: string; stderr: string }): string {
|
||||
return (result.stderr || result.stdout || "").trim();
|
||||
// Concatenate both streams so pattern matchers (isSystemdUnitNotEnabled,
|
||||
// isSystemctlMissing) can see the unit status from stdout even when
|
||||
// execFileUtf8 populates stderr with the Node error message fallback.
|
||||
return `${result.stderr} ${result.stdout}`.trim();
|
||||
}
|
||||
|
||||
function isSystemctlMissing(detail: string): boolean {
|
||||
|
||||
@@ -27,14 +27,14 @@ describe("plugin-sdk root alias", () => {
|
||||
expect(parsed.success).toBe(false);
|
||||
});
|
||||
|
||||
it("loads legacy root exports lazily through the proxy", () => {
|
||||
it("loads legacy root exports lazily through the proxy", { timeout: 240_000 }, () => {
|
||||
expect(typeof rootSdk.resolveControlCommandGate).toBe("function");
|
||||
expect(typeof rootSdk.default).toBe("object");
|
||||
expect(rootSdk.default).toBe(rootSdk);
|
||||
expect(rootSdk.__esModule).toBe(true);
|
||||
});
|
||||
|
||||
it("preserves reflection semantics for lazily resolved exports", () => {
|
||||
it("preserves reflection semantics for lazily resolved exports", { timeout: 240_000 }, () => {
|
||||
expect("resolveControlCommandGate" in rootSdk).toBe(true);
|
||||
const keys = Object.keys(rootSdk);
|
||||
expect(keys).toContain("resolveControlCommandGate");
|
||||
|
||||
Reference in New Issue
Block a user