mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-11 12:10:45 +08:00
重构主控编排与运行时预热链路,统一工作区提示词/专家调度协议并补齐 wiki 记忆注入与写回闭环。
同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。 Made-with: Cursor
This commit is contained in:
parent
4a23b715a2
commit
dbbe3add6a
14438 changed files with 2693620 additions and 2546 deletions
145
openclaw/extensions/memory-wiki/src/obsidian.ts
Normal file
145
openclaw/extensions/memory-wiki/src/obsidian.ts
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
import { execFile } from "node:child_process";
|
||||
import { constants as fsConstants } from "node:fs";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import type { ResolvedMemoryWikiConfig } from "./config.js";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
export type ObsidianCliProbe = {
|
||||
available: boolean;
|
||||
command: string | null;
|
||||
};
|
||||
|
||||
export type ObsidianCliResult = {
|
||||
command: string;
|
||||
argv: string[];
|
||||
stdout: string;
|
||||
stderr: string;
|
||||
};
|
||||
|
||||
type ObsidianCliDeps = {
|
||||
exec?: typeof execFileAsync;
|
||||
resolveCommand?: (command: string) => Promise<string | null>;
|
||||
};
|
||||
|
||||
async function isExecutableFile(inputPath: string): Promise<boolean> {
|
||||
try {
|
||||
await fs.access(inputPath, process.platform === "win32" ? fsConstants.F_OK : fsConstants.X_OK);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function resolveCommandOnPath(command: string): Promise<string | null> {
|
||||
const pathValue = process.env.PATH ?? "";
|
||||
const pathEntries = pathValue.split(path.delimiter).filter(Boolean);
|
||||
const windowsExts =
|
||||
process.platform === "win32"
|
||||
? (process.env.PATHEXT?.split(";").filter(Boolean) ?? [".EXE", ".CMD", ".BAT"])
|
||||
: [""];
|
||||
|
||||
if (command.includes(path.sep)) {
|
||||
return (await isExecutableFile(command)) ? command : null;
|
||||
}
|
||||
|
||||
for (const dir of pathEntries) {
|
||||
for (const extension of windowsExts) {
|
||||
const candidate = path.join(dir, extension ? `${command}${extension}` : command);
|
||||
if (await isExecutableFile(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildVaultPrefix(config: ResolvedMemoryWikiConfig): string[] {
|
||||
return config.obsidian.vaultName ? [`vault=${config.obsidian.vaultName}`] : [];
|
||||
}
|
||||
|
||||
export async function probeObsidianCli(
|
||||
deps?: Pick<ObsidianCliDeps, "resolveCommand">,
|
||||
): Promise<ObsidianCliProbe> {
|
||||
const resolveCommand = deps?.resolveCommand ?? resolveCommandOnPath;
|
||||
const command = await resolveCommand("obsidian");
|
||||
return {
|
||||
available: command !== null,
|
||||
command,
|
||||
};
|
||||
}
|
||||
|
||||
export async function runObsidianCli(params: {
|
||||
config: ResolvedMemoryWikiConfig;
|
||||
subcommand: string;
|
||||
args?: string[];
|
||||
deps?: ObsidianCliDeps;
|
||||
}): Promise<ObsidianCliResult> {
|
||||
const resolveCommand = params.deps?.resolveCommand ?? resolveCommandOnPath;
|
||||
const exec = params.deps?.exec ?? execFileAsync;
|
||||
const probe = await probeObsidianCli({ resolveCommand });
|
||||
if (!probe.command) {
|
||||
throw new Error("Obsidian CLI is not available on PATH.");
|
||||
}
|
||||
const argv = [...buildVaultPrefix(params.config), params.subcommand, ...(params.args ?? [])];
|
||||
const { stdout, stderr } = await exec(probe.command, argv, { encoding: "utf8" });
|
||||
return {
|
||||
command: probe.command,
|
||||
argv,
|
||||
stdout,
|
||||
stderr,
|
||||
};
|
||||
}
|
||||
|
||||
export async function runObsidianSearch(params: {
|
||||
config: ResolvedMemoryWikiConfig;
|
||||
query: string;
|
||||
deps?: ObsidianCliDeps;
|
||||
}) {
|
||||
return await runObsidianCli({
|
||||
config: params.config,
|
||||
subcommand: "search",
|
||||
args: [`query=${params.query}`],
|
||||
deps: params.deps,
|
||||
});
|
||||
}
|
||||
|
||||
export async function runObsidianOpen(params: {
|
||||
config: ResolvedMemoryWikiConfig;
|
||||
vaultPath: string;
|
||||
deps?: ObsidianCliDeps;
|
||||
}) {
|
||||
return await runObsidianCli({
|
||||
config: params.config,
|
||||
subcommand: "open",
|
||||
args: [`path=${params.vaultPath}`],
|
||||
deps: params.deps,
|
||||
});
|
||||
}
|
||||
|
||||
export async function runObsidianCommand(params: {
|
||||
config: ResolvedMemoryWikiConfig;
|
||||
id: string;
|
||||
deps?: ObsidianCliDeps;
|
||||
}) {
|
||||
return await runObsidianCli({
|
||||
config: params.config,
|
||||
subcommand: "command",
|
||||
args: [`id=${params.id}`],
|
||||
deps: params.deps,
|
||||
});
|
||||
}
|
||||
|
||||
export async function runObsidianDaily(params: {
|
||||
config: ResolvedMemoryWikiConfig;
|
||||
deps?: ObsidianCliDeps;
|
||||
}) {
|
||||
return await runObsidianCli({
|
||||
config: params.config,
|
||||
subcommand: "daily",
|
||||
deps: params.deps,
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue