fix: clean up Hermes-visible OpenClaw leftovers (#97)
* cleanup openclaw session leftovers - hermes can breathe now * fix: load hermes adapter env from .env * fix: redact secrets from hermes adapter error output * addressed review findings * address luke findings #2
This commit is contained in:
@@ -1110,6 +1110,7 @@ describe("AgentSettingsPanel", () => {
|
||||
cronDeleteBusyJobId: null,
|
||||
onRunCronJob: vi.fn(),
|
||||
onDeleteCronJob: vi.fn(),
|
||||
adapterType: "openclaw",
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1117,6 +1118,29 @@ describe("AgentSettingsPanel", () => {
|
||||
expect(screen.getByText("Heartbeat automation controls are coming soon.")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("hides_heartbeat_coming_soon_for_hermes", () => {
|
||||
render(
|
||||
createElement(AgentSettingsPanel, {
|
||||
agent: createAgent(),
|
||||
mode: "automations",
|
||||
onClose: vi.fn(),
|
||||
onDelete: vi.fn(),
|
||||
onToolCallingToggle: vi.fn(),
|
||||
onThinkingTracesToggle: vi.fn(),
|
||||
cronJobs: [createCronJob("job-1")],
|
||||
cronLoading: false,
|
||||
cronError: null,
|
||||
cronRunBusyJobId: null,
|
||||
cronDeleteBusyJobId: null,
|
||||
onRunCronJob: vi.fn(),
|
||||
onDeleteCronJob: vi.fn(),
|
||||
adapterType: "hermes",
|
||||
})
|
||||
);
|
||||
|
||||
expect(screen.queryByTestId("agent-settings-heartbeat-coming-soon")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows_control_ui_section_in_advanced_mode", () => {
|
||||
render(
|
||||
createElement(AgentSettingsPanel, {
|
||||
@@ -1133,6 +1157,7 @@ describe("AgentSettingsPanel", () => {
|
||||
cronDeleteBusyJobId: null,
|
||||
onRunCronJob: vi.fn(),
|
||||
onDeleteCronJob: vi.fn(),
|
||||
adapterType: "openclaw",
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1140,6 +1165,30 @@ describe("AgentSettingsPanel", () => {
|
||||
expect(screen.getByRole("button", { name: "Open Full Control UI" })).toBeDisabled();
|
||||
});
|
||||
|
||||
it("hides_control_ui_section_for_hermes", () => {
|
||||
render(
|
||||
createElement(AgentSettingsPanel, {
|
||||
agent: createAgent(),
|
||||
mode: "advanced",
|
||||
onClose: vi.fn(),
|
||||
onDelete: vi.fn(),
|
||||
onToolCallingToggle: vi.fn(),
|
||||
onThinkingTracesToggle: vi.fn(),
|
||||
cronJobs: [],
|
||||
cronLoading: false,
|
||||
cronError: null,
|
||||
cronRunBusyJobId: null,
|
||||
cronDeleteBusyJobId: null,
|
||||
onRunCronJob: vi.fn(),
|
||||
onDeleteCronJob: vi.fn(),
|
||||
adapterType: "hermes",
|
||||
})
|
||||
);
|
||||
|
||||
expect(screen.queryByTestId("agent-settings-control-ui")).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Open Full Control UI" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders_enabled_control_ui_link_when_available", () => {
|
||||
render(
|
||||
createElement(AgentSettingsPanel, {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
describe("loadLocalGatewayDefaults with CLAW3D_GATEWAY_URL", () => {
|
||||
const originalEnv = { ...process.env };
|
||||
@@ -17,7 +20,14 @@ describe("loadLocalGatewayDefaults with CLAW3D_GATEWAY_URL", () => {
|
||||
"../../src/lib/studio/settings-store"
|
||||
);
|
||||
const result = loadLocalGatewayDefaults();
|
||||
expect(result).toEqual({ url: "ws://my-gateway:18789", token: "my-token" });
|
||||
expect(result).toEqual({
|
||||
url: "ws://my-gateway:18789",
|
||||
token: "my-token",
|
||||
adapterType: "openclaw",
|
||||
profiles: {
|
||||
openclaw: { url: "ws://my-gateway:18789", token: "my-token" },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("returns env-based defaults with empty token when only URL is set", async () => {
|
||||
@@ -28,7 +38,14 @@ describe("loadLocalGatewayDefaults with CLAW3D_GATEWAY_URL", () => {
|
||||
"../../src/lib/studio/settings-store"
|
||||
);
|
||||
const result = loadLocalGatewayDefaults();
|
||||
expect(result).toEqual({ url: "ws://my-gateway:18789", token: "" });
|
||||
expect(result).toEqual({
|
||||
url: "ws://my-gateway:18789",
|
||||
token: "",
|
||||
adapterType: "openclaw",
|
||||
profiles: {
|
||||
openclaw: { url: "ws://my-gateway:18789", token: "" },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("returns null when no env var and no openclaw.json", async () => {
|
||||
@@ -57,4 +74,110 @@ describe("loadLocalGatewayDefaults with CLAW3D_GATEWAY_URL", () => {
|
||||
}
|
||||
// If no file exists in CI, it falls back to env — that's also correct
|
||||
});
|
||||
|
||||
it("uses CLAW3D_GATEWAY_ADAPTER_TYPE for Hermes env defaults", async () => {
|
||||
process.env.CLAW3D_GATEWAY_URL = "ws://my-hermes:18789";
|
||||
process.env.CLAW3D_GATEWAY_ADAPTER_TYPE = "hermes";
|
||||
delete process.env.CLAW3D_GATEWAY_TOKEN;
|
||||
process.env.OPENCLAW_STATE_DIR = "/tmp/claw3d-test-nonexistent-" + Date.now();
|
||||
const { loadLocalGatewayDefaults } = await import(
|
||||
"../../src/lib/studio/settings-store"
|
||||
);
|
||||
const result = loadLocalGatewayDefaults();
|
||||
expect(result).toEqual({
|
||||
url: "ws://my-hermes:18789",
|
||||
token: "",
|
||||
adapterType: "hermes",
|
||||
profiles: {
|
||||
hermes: { url: "ws://my-hermes:18789", token: "" },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("exposes local Hermes adapter defaults when only HERMES_ADAPTER_PORT is set", async () => {
|
||||
delete process.env.CLAW3D_GATEWAY_URL;
|
||||
delete process.env.CLAW3D_GATEWAY_TOKEN;
|
||||
process.env.HERMES_ADAPTER_PORT = "19444";
|
||||
process.env.OPENCLAW_STATE_DIR = "/tmp/claw3d-test-nonexistent-" + Date.now();
|
||||
const { loadLocalGatewayDefaults } = await import(
|
||||
"../../src/lib/studio/settings-store"
|
||||
);
|
||||
const result = loadLocalGatewayDefaults();
|
||||
expect(result).toEqual({
|
||||
url: "ws://localhost:19444",
|
||||
token: "",
|
||||
adapterType: "hermes",
|
||||
profiles: {
|
||||
hermes: { url: "ws://localhost:19444", token: "" },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("merges Hermes adapter defaults into file-backed OpenClaw defaults", async () => {
|
||||
delete process.env.CLAW3D_GATEWAY_URL;
|
||||
delete process.env.CLAW3D_GATEWAY_TOKEN;
|
||||
delete process.env.CLAW3D_GATEWAY_ADAPTER_TYPE;
|
||||
process.env.HERMES_ADAPTER_PORT = "19444";
|
||||
|
||||
const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "claw3d-gateway-defaults-"));
|
||||
process.env.OPENCLAW_STATE_DIR = stateDir;
|
||||
fs.writeFileSync(
|
||||
path.join(stateDir, "openclaw.json"),
|
||||
JSON.stringify({
|
||||
gateway: {
|
||||
port: 18789,
|
||||
auth: { token: "file-token" },
|
||||
},
|
||||
}),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
const { loadLocalGatewayDefaults } = await import(
|
||||
"../../src/lib/studio/settings-store"
|
||||
);
|
||||
const result = loadLocalGatewayDefaults();
|
||||
|
||||
expect(result).toEqual({
|
||||
url: "ws://localhost:18789",
|
||||
token: "file-token",
|
||||
adapterType: "openclaw",
|
||||
profiles: {
|
||||
openclaw: { url: "ws://localhost:18789", token: "file-token" },
|
||||
hermes: { url: "ws://localhost:19444", token: "" },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps file-backed openclaw profile when CLAW3D_GATEWAY_URL is also set", async () => {
|
||||
process.env.CLAW3D_GATEWAY_URL = "ws://env-gateway:19999";
|
||||
process.env.CLAW3D_GATEWAY_TOKEN = "env-token";
|
||||
delete process.env.CLAW3D_GATEWAY_ADAPTER_TYPE;
|
||||
|
||||
const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "claw3d-gateway-defaults-"));
|
||||
process.env.OPENCLAW_STATE_DIR = stateDir;
|
||||
fs.writeFileSync(
|
||||
path.join(stateDir, "openclaw.json"),
|
||||
JSON.stringify({
|
||||
gateway: {
|
||||
port: 18789,
|
||||
auth: { token: "file-token" },
|
||||
},
|
||||
}),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
const { loadLocalGatewayDefaults } = await import(
|
||||
"../../src/lib/studio/settings-store"
|
||||
);
|
||||
const result = loadLocalGatewayDefaults();
|
||||
|
||||
expect(result).toEqual({
|
||||
url: "ws://localhost:18789",
|
||||
token: "file-token",
|
||||
adapterType: "openclaw",
|
||||
profiles: {
|
||||
openclaw: { url: "ws://localhost:18789", token: "file-token" },
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,8 +47,20 @@ describe("studio settings route", () => {
|
||||
|
||||
const response = await GET();
|
||||
const body = (await response.json()) as {
|
||||
settings?: { gateway?: { url?: string; tokenConfigured?: boolean } | null };
|
||||
localGatewayDefaults?: { url?: string; tokenConfigured?: boolean } | null;
|
||||
settings?: {
|
||||
gateway?: {
|
||||
url?: string;
|
||||
tokenConfigured?: boolean;
|
||||
adapterType?: string;
|
||||
profiles?: Record<string, { url?: string; tokenConfigured?: boolean }>;
|
||||
} | null;
|
||||
};
|
||||
localGatewayDefaults?: {
|
||||
url?: string;
|
||||
tokenConfigured?: boolean;
|
||||
adapterType?: string;
|
||||
profiles?: Record<string, { url?: string; tokenConfigured?: boolean }>;
|
||||
} | null;
|
||||
};
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
@@ -56,11 +68,23 @@ describe("studio settings route", () => {
|
||||
url: "ws://localhost:18791",
|
||||
tokenConfigured: true,
|
||||
adapterType: "openclaw",
|
||||
profiles: {
|
||||
openclaw: {
|
||||
url: "ws://localhost:18791",
|
||||
tokenConfigured: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(body.settings?.gateway).toEqual({
|
||||
url: "ws://localhost:18791",
|
||||
tokenConfigured: true,
|
||||
adapterType: "openclaw",
|
||||
profiles: {
|
||||
openclaw: {
|
||||
url: "ws://localhost:18791",
|
||||
tokenConfigured: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user