mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 03:51:25 +00:00
fix(memory): resolve qmd Windows shim commands
This commit is contained in:
@@ -729,6 +729,27 @@ describe("QmdMemoryManager", () => {
|
||||
await manager.close();
|
||||
});
|
||||
|
||||
it("uses qmd.cmd on Windows when qmd command is bare", async () => {
|
||||
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
|
||||
try {
|
||||
const { manager } = await createManager({ mode: "status" });
|
||||
await manager.sync({ reason: "manual" });
|
||||
|
||||
const qmdCalls = spawnMock.mock.calls.filter((call: unknown[]) => {
|
||||
const args = call[1] as string[] | undefined;
|
||||
return Array.isArray(args) && args.length > 0;
|
||||
});
|
||||
expect(qmdCalls.length).toBeGreaterThan(0);
|
||||
for (const call of qmdCalls) {
|
||||
expect(call[0]).toBe("qmd.cmd");
|
||||
}
|
||||
|
||||
await manager.close();
|
||||
} finally {
|
||||
platformSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("normalizes mixed Han-script BM25 queries before qmd search", async () => {
|
||||
cfg = {
|
||||
...cfg,
|
||||
@@ -1194,6 +1215,47 @@ describe("QmdMemoryManager", () => {
|
||||
await manager.close();
|
||||
});
|
||||
|
||||
it("uses mcporter.cmd on Windows when mcporter bridge is enabled", async () => {
|
||||
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
|
||||
try {
|
||||
cfg = {
|
||||
...cfg,
|
||||
memory: {
|
||||
backend: "qmd",
|
||||
qmd: {
|
||||
includeDefaultMemory: false,
|
||||
update: { interval: "0s", debounceMs: 60_000, onBoot: false },
|
||||
paths: [{ path: workspaceDir, pattern: "**/*.md", name: "workspace" }],
|
||||
mcporter: { enabled: true, serverName: "qmd", startDaemon: false },
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
spawnMock.mockImplementation((_cmd: string, args: string[]) => {
|
||||
const child = createMockChild({ autoClose: false });
|
||||
if (args[0] === "call") {
|
||||
emitAndClose(child, "stdout", JSON.stringify({ results: [] }));
|
||||
return child;
|
||||
}
|
||||
emitAndClose(child, "stdout", "[]");
|
||||
return child;
|
||||
});
|
||||
|
||||
const { manager } = await createManager();
|
||||
await manager.search("hello", { sessionKey: "agent:main:slack:dm:u123" });
|
||||
|
||||
const mcporterCall = spawnMock.mock.calls.find(
|
||||
(call: unknown[]) => (call[1] as string[] | undefined)?.[0] === "call",
|
||||
);
|
||||
expect(mcporterCall).toBeDefined();
|
||||
expect(mcporterCall?.[0]).toBe("mcporter.cmd");
|
||||
|
||||
await manager.close();
|
||||
} finally {
|
||||
platformSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("passes manager-scoped XDG env to mcporter commands", async () => {
|
||||
cfg = {
|
||||
...cfg,
|
||||
|
||||
@@ -46,6 +46,25 @@ const QMD_BM25_HAN_KEYWORD_LIMIT = 12;
|
||||
|
||||
let qmdEmbedQueueTail: Promise<void> = Promise.resolve();
|
||||
|
||||
function resolveWindowsCommandShim(command: string): string {
|
||||
if (process.platform !== "win32") {
|
||||
return command;
|
||||
}
|
||||
const trimmed = command.trim();
|
||||
if (!trimmed) {
|
||||
return command;
|
||||
}
|
||||
const ext = path.extname(trimmed).toLowerCase();
|
||||
if (ext === ".cmd" || ext === ".exe" || ext === ".bat") {
|
||||
return command;
|
||||
}
|
||||
const base = path.basename(trimmed).toLowerCase();
|
||||
if (base === "qmd" || base === "mcporter") {
|
||||
return `${trimmed}.cmd`;
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
function hasHanScript(value: string): boolean {
|
||||
return HAN_SCRIPT_RE.test(value);
|
||||
}
|
||||
@@ -943,7 +962,7 @@ export class QmdMemoryManager implements MemorySearchManager {
|
||||
opts?: { timeoutMs?: number },
|
||||
): Promise<{ stdout: string; stderr: string }> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const child = spawn(this.qmd.command, args, {
|
||||
const child = spawn(resolveWindowsCommandShim(this.qmd.command), args, {
|
||||
env: this.env,
|
||||
cwd: this.workspaceDir,
|
||||
});
|
||||
@@ -1034,7 +1053,7 @@ export class QmdMemoryManager implements MemorySearchManager {
|
||||
opts?: { timeoutMs?: number },
|
||||
): Promise<{ stdout: string; stderr: string }> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const child = spawn("mcporter", args, {
|
||||
const child = spawn(resolveWindowsCommandShim("mcporter"), args, {
|
||||
// Keep mcporter and direct qmd commands on the same agent-scoped XDG state.
|
||||
env: this.env,
|
||||
cwd: this.workspaceDir,
|
||||
|
||||
Reference in New Issue
Block a user