mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 04:50:44 +00:00
fix: explain missing git during plugin install
This commit is contained in:
@@ -80,6 +80,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Gateway/watch: suppress sync-I/O trace output during `pnpm gateway:watch --benchmark` unless explicitly requested, so CPU profiling no longer floods the terminal with stack traces.
|
||||
- Gateway/watch: when benchmark sync-I/O tracing is explicitly enabled, tee trace blocks to the benchmark output log and filter them from the terminal pane while keeping normal Gateway logs visible.
|
||||
- Plugins/runtime-deps: include `json5` in the memory-core plugin runtime dependency set so packaged `memory_search` sandboxes can resolve generated OpenClaw runtime chunks that parse JSON5 config. Fixes #77461.
|
||||
- Plugins/Windows: show a Git install hint when npm plugin installation fails with `spawn git ENOENT`, and document the WhatsApp plugin's Git-on-PATH requirement for Baileys/libsignal installs.
|
||||
- Codex harness: preserve app-server usage-limit reset details and deliver OpenClaw-owned runtime failure notices through tool-only source-reply mode, so Telegram and other chat channels tell users when Codex subscription limits or API failures block a turn instead of going silent. (#77557) Thanks @pashpashpash.
|
||||
- Agents/OpenAI: default direct OpenAI Responses models to the SSE transport instead of WebSocket auto-selection, preventing pi runtime chat turns from hanging on servers where the WebSocket path stalls while the OpenAI HTTP stream works. Thanks @vincentkoc.
|
||||
- Plugins/update: repair missing plugin-local `openclaw` peer links before skipping unchanged npm plugin updates, so current external Codex installs can recover `openclaw/plugin-sdk/*` resolution during OTA repair. (#77544) Thanks @ProspectOre.
|
||||
|
||||
@@ -26,6 +26,16 @@ openclaw plugins install @openclaw/whatsapp
|
||||
Use the bare package to follow the current official release tag. Pin an exact
|
||||
version only when you need a reproducible install.
|
||||
|
||||
On Windows, the WhatsApp plugin needs Git on `PATH` during npm install because
|
||||
one of its Baileys/libsignal dependencies is fetched from a git URL. Install
|
||||
Git for Windows, then restart the shell and rerun the install:
|
||||
|
||||
```powershell
|
||||
winget install --id Git.Git -e
|
||||
```
|
||||
|
||||
Portable Git also works if its `bin` directory is on `PATH`.
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Pairing" icon="link" href="/channels/pairing">
|
||||
Default DM policy is pairing for unknown senders.
|
||||
|
||||
@@ -18,6 +18,16 @@ Adds the WhatsApp channel surface for sending and receiving OpenClaw messages.
|
||||
|
||||
channels: whatsapp
|
||||
|
||||
## Windows install note
|
||||
|
||||
On Windows, the WhatsApp plugin needs Git on `PATH` during npm install because one of its Baileys/libsignal dependencies is fetched from a git URL. Install Git for Windows, then restart the shell and rerun the install:
|
||||
|
||||
```powershell
|
||||
winget install --id Git.Git -e
|
||||
```
|
||||
|
||||
Portable Git also works if its `bin` directory is on `PATH`.
|
||||
|
||||
## Related docs
|
||||
|
||||
- [whatsapp](/channels/whatsapp)
|
||||
|
||||
@@ -28,6 +28,20 @@ const PLUGIN_DOC_ALIASES = new Map([
|
||||
["tavily", "/tools/tavily"],
|
||||
["tokenjuice", "/tools/tokenjuice"],
|
||||
]);
|
||||
const PLUGIN_REFERENCE_EXTRA_SECTIONS = new Map([
|
||||
[
|
||||
"whatsapp",
|
||||
`## Windows install note
|
||||
|
||||
On Windows, the WhatsApp plugin needs Git on \`PATH\` during npm install because one of its Baileys/libsignal dependencies is fetched from a git URL. Install Git for Windows, then restart the shell and rerun the install:
|
||||
|
||||
\`\`\`powershell
|
||||
winget install --id Git.Git -e
|
||||
\`\`\`
|
||||
|
||||
Portable Git also works if its \`bin\` directory is on \`PATH\`.`,
|
||||
],
|
||||
]);
|
||||
|
||||
function readJson(relativePath) {
|
||||
return JSON.parse(fs.readFileSync(path.join(ROOT, relativePath), "utf8"));
|
||||
@@ -376,6 +390,7 @@ ${record.docs.map((link) => `- ${docLink(link)}`).join("\n")}`;
|
||||
|
||||
function renderReferencePage(record) {
|
||||
const relatedDocs = renderRelatedDocs(record);
|
||||
const extraSections = PLUGIN_REFERENCE_EXTRA_SECTIONS.get(record.id);
|
||||
return `---
|
||||
summary: "${record.description.replaceAll('"', '\\"')}"
|
||||
read_when:
|
||||
@@ -394,7 +409,7 @@ ${record.description}
|
||||
|
||||
## Surface
|
||||
|
||||
${record.surface}${relatedDocs ? `\n\n${relatedDocs}` : ""}
|
||||
${record.surface}${extraSections ? `\n\n${extraSections}` : ""}${relatedDocs ? `\n\n${relatedDocs}` : ""}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1096,6 +1096,34 @@ describe("plugins cli install", () => {
|
||||
expect(runtimeErrors.at(-1)).toContain("npm install failed");
|
||||
});
|
||||
|
||||
it("adds a Git PATH hint when npm plugin dependency install cannot spawn git", async () => {
|
||||
loadConfig.mockReturnValue({} as OpenClawConfig);
|
||||
installPluginFromNpmSpec.mockResolvedValue({
|
||||
ok: false,
|
||||
error: [
|
||||
"npm install failed:",
|
||||
"npm error code ENOENT",
|
||||
"npm error syscall spawn git",
|
||||
"npm error path git",
|
||||
].join("\n"),
|
||||
});
|
||||
installHooksFromNpmSpec.mockResolvedValue({
|
||||
ok: false,
|
||||
error: "package.json missing openclaw.hooks",
|
||||
});
|
||||
|
||||
await expect(
|
||||
runPluginsCommand(["plugins", "install", "npm:@openclaw/whatsapp"]),
|
||||
).rejects.toThrow("__exit__:1");
|
||||
|
||||
expect(installPluginFromClawHub).not.toHaveBeenCalled();
|
||||
expect(runtimeErrors.at(-1)).toContain(
|
||||
"one of this plugin's npm dependencies is fetched from a git URL",
|
||||
);
|
||||
expect(runtimeErrors.at(-1)).toContain("winget install --id Git.Git -e");
|
||||
expect(runtimeErrors.at(-1)).toContain("Also not a valid hook pack");
|
||||
});
|
||||
|
||||
it("does not resolve npm: prefixed bundled plugin ids through bundled installs", async () => {
|
||||
loadConfig.mockReturnValue({ plugins: { load: { paths: [] } } } as OpenClawConfig);
|
||||
installPluginFromNpmSpec.mockResolvedValue({
|
||||
|
||||
@@ -176,16 +176,36 @@ export function formatPluginInstallWithHookFallbackError(
|
||||
pluginError: string,
|
||||
hookError: string,
|
||||
): string {
|
||||
const formattedPluginError = formatPluginInstallAttemptError(pluginError);
|
||||
const formattedHookError = formatPluginInstallAttemptError(hookError);
|
||||
if (/plugin already exists: .+ \(delete it first\)/.test(pluginError)) {
|
||||
return `${pluginError}\nUse \`openclaw plugins update <id-or-npm-spec>\` to upgrade the tracked plugin, or rerun install with \`--force\` to replace it.`;
|
||||
return `${formattedPluginError}\nUse \`openclaw plugins update <id-or-npm-spec>\` to upgrade the tracked plugin, or rerun install with \`--force\` to replace it.`;
|
||||
}
|
||||
if (
|
||||
pluginError.startsWith("Invalid extensions directory:") ||
|
||||
pluginError === "Invalid path: must stay within extensions directory"
|
||||
) {
|
||||
return pluginError;
|
||||
return formattedPluginError;
|
||||
}
|
||||
return `${pluginError}\nAlso not a valid hook pack: ${hookError}`;
|
||||
return `${formattedPluginError}\nAlso not a valid hook pack: ${formattedHookError}`;
|
||||
}
|
||||
|
||||
const MISSING_GIT_FOR_NPM_DEPENDENCY_HINT =
|
||||
"Git is required because one of this plugin's npm dependencies is fetched from a git URL, but `git` was not found on PATH. Install Git and rerun the install. On Windows, use `winget install --id Git.Git -e` or add a portable Git `bin` directory to PATH.";
|
||||
|
||||
function formatPluginInstallAttemptError(error: string): string {
|
||||
if (!isMissingGitForNpmDependencyError(error)) {
|
||||
return error;
|
||||
}
|
||||
if (error.includes(MISSING_GIT_FOR_NPM_DEPENDENCY_HINT)) {
|
||||
return error;
|
||||
}
|
||||
return `${error}\n\n${MISSING_GIT_FOR_NPM_DEPENDENCY_HINT}`;
|
||||
}
|
||||
|
||||
function isMissingGitForNpmDependencyError(error: string): boolean {
|
||||
const normalized = normalizeLowercaseStringOrEmpty(error);
|
||||
return /\bspawn\s+git\b/u.test(normalized) && /\benoent\b/u.test(normalized);
|
||||
}
|
||||
|
||||
export function logHookPackRestartHint(runtime: RuntimeEnv = defaultRuntime) {
|
||||
|
||||
Reference in New Issue
Block a user