Fix command handlers to return { text } per OpenClaw plugin API (#1115)

The OpenClaw plugin API requires command handlers to return
{ text: string } objects. Returning plain strings causes
"reply missing text/media" errors and commands silently fail
to send responses to Telegram/other channels.
This commit is contained in:
Daniel M.
2026-02-16 06:32:04 +01:00
committed by GitHub
parent cd31eaf572
commit be474ea595
2 changed files with 20 additions and 20 deletions
+11 -11
View File
@@ -168,7 +168,7 @@ describe("claudeMemPlugin", () => {
claudeMemPlugin(api);
const result = await getCommand().handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed", config: {} });
assert.ok(result.includes("not configured"));
assert.ok(result.text.includes("not configured"));
});
it("returns status when no args", async () => {
@@ -178,10 +178,10 @@ describe("claudeMemPlugin", () => {
claudeMemPlugin(api);
const result = await getCommand().handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed", config: {} });
assert.ok(result.includes("Enabled: yes"));
assert.ok(result.includes("Channel: telegram"));
assert.ok(result.includes("Target: 123"));
assert.ok(result.includes("Connection:"));
assert.ok(result.text.includes("Enabled: yes"));
assert.ok(result.text.includes("Channel: telegram"));
assert.ok(result.text.includes("Target: 123"));
assert.ok(result.text.includes("Connection:"));
});
it("handles 'on' argument", async () => {
@@ -191,7 +191,7 @@ describe("claudeMemPlugin", () => {
claudeMemPlugin(api);
const result = await getCommand().handler({ args: "on", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed on", config: {} });
assert.ok(result.includes("enable requested"));
assert.ok(result.text.includes("enable requested"));
assert.ok(logs.some((l) => l.includes("enable requested")));
});
@@ -202,7 +202,7 @@ describe("claudeMemPlugin", () => {
claudeMemPlugin(api);
const result = await getCommand().handler({ args: "off", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed off", config: {} });
assert.ok(result.includes("disable requested"));
assert.ok(result.text.includes("disable requested"));
assert.ok(logs.some((l) => l.includes("disable requested")));
});
@@ -213,7 +213,7 @@ describe("claudeMemPlugin", () => {
claudeMemPlugin(api);
const result = await getCommand().handler({ args: "", channel: "slack", isAuthorizedSender: true, commandBody: "/claude-mem-feed", config: {} });
assert.ok(result.includes("Connection: disconnected"));
assert.ok(result.text.includes("Connection: disconnected"));
});
});
});
@@ -493,8 +493,8 @@ describe("Observation I/O event handlers", () => {
assert.ok(statusCmd, "status command should exist");
const result = await statusCmd.handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-status", config: {} });
assert.ok(result.includes("Status: ok"));
assert.ok(result.includes(`Port: ${workerPort}`));
assert.ok(result.text.includes("Status: ok"));
assert.ok(result.text.includes(`Port: ${workerPort}`));
});
it("claude-mem-status reports unreachable when worker is down", async () => {
@@ -506,7 +506,7 @@ describe("Observation I/O event handlers", () => {
const statusCmd = getCommand("claude-mem-status");
const result = await statusCmd.handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-status", config: {} });
assert.ok(result.includes("unreachable"));
assert.ok(result.text.includes("unreachable"));
});
it("reuses same contentSessionId for same sessionKey", async () => {
+9 -9
View File
@@ -816,28 +816,28 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
const feedConfig = userConfig.observationFeed;
if (!feedConfig) {
return "Observation feed not configured. Add observationFeed to your plugin config.";
return { text: "Observation feed not configured. Add observationFeed to your plugin config." };
}
const arg = ctx.args?.trim();
if (arg === "on") {
api.logger.info("[claude-mem] Feed enable requested via command");
return "Feed enable requested. Update observationFeed.enabled in your plugin config to persist.";
return { text: "Feed enable requested. Update observationFeed.enabled in your plugin config to persist." };
}
if (arg === "off") {
api.logger.info("[claude-mem] Feed disable requested via command");
return "Feed disable requested. Update observationFeed.enabled in your plugin config to persist.";
return { text: "Feed disable requested. Update observationFeed.enabled in your plugin config to persist." };
}
return [
return { text: [
"Claude-Mem Observation Feed",
`Enabled: ${feedConfig.enabled ? "yes" : "no"}`,
`Channel: ${feedConfig.channel || "not set"}`,
`Target: ${feedConfig.to || "not set"}`,
`Connection: ${connectionState}`,
].join("\n");
].join("\n") };
},
});
@@ -985,20 +985,20 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
handler: async () => {
const healthText = await workerGetText(workerPort, "/api/health", api.logger);
if (!healthText) {
return `Claude-Mem worker unreachable at port ${workerPort}`;
return { text: `Claude-Mem worker unreachable at port ${workerPort}` };
}
try {
const health = JSON.parse(healthText);
return [
return { text: [
"Claude-Mem Worker Status",
`Status: ${health.status || "unknown"}`,
`Port: ${workerPort}`,
`Active sessions: ${sessionIds.size}`,
`Observation feed: ${connectionState}`,
].join("\n");
].join("\n") };
} catch {
return `Claude-Mem worker responded but returned unexpected data`;
return { text: `Claude-Mem worker responded but returned unexpected data` };
}
},
});