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:
+11
-11
@@ -168,7 +168,7 @@ describe("claudeMemPlugin", () => {
|
|||||||
claudeMemPlugin(api);
|
claudeMemPlugin(api);
|
||||||
|
|
||||||
const result = await getCommand().handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed", config: {} });
|
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 () => {
|
it("returns status when no args", async () => {
|
||||||
@@ -178,10 +178,10 @@ describe("claudeMemPlugin", () => {
|
|||||||
claudeMemPlugin(api);
|
claudeMemPlugin(api);
|
||||||
|
|
||||||
const result = await getCommand().handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed", config: {} });
|
const result = await getCommand().handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed", config: {} });
|
||||||
assert.ok(result.includes("Enabled: yes"));
|
assert.ok(result.text.includes("Enabled: yes"));
|
||||||
assert.ok(result.includes("Channel: telegram"));
|
assert.ok(result.text.includes("Channel: telegram"));
|
||||||
assert.ok(result.includes("Target: 123"));
|
assert.ok(result.text.includes("Target: 123"));
|
||||||
assert.ok(result.includes("Connection:"));
|
assert.ok(result.text.includes("Connection:"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("handles 'on' argument", async () => {
|
it("handles 'on' argument", async () => {
|
||||||
@@ -191,7 +191,7 @@ describe("claudeMemPlugin", () => {
|
|||||||
claudeMemPlugin(api);
|
claudeMemPlugin(api);
|
||||||
|
|
||||||
const result = await getCommand().handler({ args: "on", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed on", config: {} });
|
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")));
|
assert.ok(logs.some((l) => l.includes("enable requested")));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ describe("claudeMemPlugin", () => {
|
|||||||
claudeMemPlugin(api);
|
claudeMemPlugin(api);
|
||||||
|
|
||||||
const result = await getCommand().handler({ args: "off", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-feed off", config: {} });
|
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")));
|
assert.ok(logs.some((l) => l.includes("disable requested")));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ describe("claudeMemPlugin", () => {
|
|||||||
claudeMemPlugin(api);
|
claudeMemPlugin(api);
|
||||||
|
|
||||||
const result = await getCommand().handler({ args: "", channel: "slack", isAuthorizedSender: true, commandBody: "/claude-mem-feed", config: {} });
|
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");
|
assert.ok(statusCmd, "status command should exist");
|
||||||
|
|
||||||
const result = await statusCmd.handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-status", config: {} });
|
const result = await statusCmd.handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-status", config: {} });
|
||||||
assert.ok(result.includes("Status: ok"));
|
assert.ok(result.text.includes("Status: ok"));
|
||||||
assert.ok(result.includes(`Port: ${workerPort}`));
|
assert.ok(result.text.includes(`Port: ${workerPort}`));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("claude-mem-status reports unreachable when worker is down", async () => {
|
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 statusCmd = getCommand("claude-mem-status");
|
||||||
const result = await statusCmd.handler({ args: "", channel: "telegram", isAuthorizedSender: true, commandBody: "/claude-mem-status", config: {} });
|
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 () => {
|
it("reuses same contentSessionId for same sessionKey", async () => {
|
||||||
|
|||||||
@@ -816,28 +816,28 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
|
|||||||
const feedConfig = userConfig.observationFeed;
|
const feedConfig = userConfig.observationFeed;
|
||||||
|
|
||||||
if (!feedConfig) {
|
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();
|
const arg = ctx.args?.trim();
|
||||||
|
|
||||||
if (arg === "on") {
|
if (arg === "on") {
|
||||||
api.logger.info("[claude-mem] Feed enable requested via command");
|
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") {
|
if (arg === "off") {
|
||||||
api.logger.info("[claude-mem] Feed disable requested via command");
|
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",
|
"Claude-Mem Observation Feed",
|
||||||
`Enabled: ${feedConfig.enabled ? "yes" : "no"}`,
|
`Enabled: ${feedConfig.enabled ? "yes" : "no"}`,
|
||||||
`Channel: ${feedConfig.channel || "not set"}`,
|
`Channel: ${feedConfig.channel || "not set"}`,
|
||||||
`Target: ${feedConfig.to || "not set"}`,
|
`Target: ${feedConfig.to || "not set"}`,
|
||||||
`Connection: ${connectionState}`,
|
`Connection: ${connectionState}`,
|
||||||
].join("\n");
|
].join("\n") };
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -985,20 +985,20 @@ export default function claudeMemPlugin(api: OpenClawPluginApi): void {
|
|||||||
handler: async () => {
|
handler: async () => {
|
||||||
const healthText = await workerGetText(workerPort, "/api/health", api.logger);
|
const healthText = await workerGetText(workerPort, "/api/health", api.logger);
|
||||||
if (!healthText) {
|
if (!healthText) {
|
||||||
return `Claude-Mem worker unreachable at port ${workerPort}`;
|
return { text: `Claude-Mem worker unreachable at port ${workerPort}` };
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const health = JSON.parse(healthText);
|
const health = JSON.parse(healthText);
|
||||||
return [
|
return { text: [
|
||||||
"Claude-Mem Worker Status",
|
"Claude-Mem Worker Status",
|
||||||
`Status: ${health.status || "unknown"}`,
|
`Status: ${health.status || "unknown"}`,
|
||||||
`Port: ${workerPort}`,
|
`Port: ${workerPort}`,
|
||||||
`Active sessions: ${sessionIds.size}`,
|
`Active sessions: ${sessionIds.size}`,
|
||||||
`Observation feed: ${connectionState}`,
|
`Observation feed: ${connectionState}`,
|
||||||
].join("\n");
|
].join("\n") };
|
||||||
} catch {
|
} catch {
|
||||||
return `Claude-Mem worker responded but returned unexpected data`;
|
return { text: `Claude-Mem worker responded but returned unexpected data` };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user