fix: explain missing git during plugin install · openclaw/openclaw@a91c17c
steipete
·
2026-05-05
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -80,6 +80,7 @@ Docs: https://docs.openclaw.ai
|
80 | 80 | - 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. |
81 | 81 | - 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. |
82 | 82 | - 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. |
| 83 | +- 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. |
83 | 84 | - 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. |
84 | 85 | - 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. |
85 | 86 | - 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. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -26,6 +26,16 @@ openclaw plugins install @openclaw/whatsapp
|
26 | 26 | Use the bare package to follow the current official release tag. Pin an exact |
27 | 27 | version only when you need a reproducible install. |
28 | 28 | |
| 29 | +On Windows, the WhatsApp plugin needs Git on `PATH` during npm install because |
| 30 | +one of its Baileys/libsignal dependencies is fetched from a git URL. Install |
| 31 | +Git for Windows, then restart the shell and rerun the install: |
| 32 | + |
| 33 | +```powershell |
| 34 | +winget install --id Git.Git -e |
| 35 | +``` |
| 36 | + |
| 37 | +Portable Git also works if its `bin` directory is on `PATH`. |
| 38 | + |
29 | 39 | <CardGroup cols={3}> |
30 | 40 | <Card title="Pairing" icon="link" href="/channels/pairing"> |
31 | 41 | Default DM policy is pairing for unknown senders. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -18,6 +18,16 @@ Adds the WhatsApp channel surface for sending and receiving OpenClaw messages.
|
18 | 18 | |
19 | 19 | channels: whatsapp |
20 | 20 | |
| 21 | +## Windows install note |
| 22 | + |
| 23 | +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: |
| 24 | + |
| 25 | +```powershell |
| 26 | +winget install --id Git.Git -e |
| 27 | +``` |
| 28 | + |
| 29 | +Portable Git also works if its `bin` directory is on `PATH`. |
| 30 | + |
21 | 31 | ## Related docs |
22 | 32 | |
23 | 33 | - [whatsapp](/channels/whatsapp) |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -28,6 +28,20 @@ const PLUGIN_DOC_ALIASES = new Map([
|
28 | 28 | ["tavily", "/tools/tavily"], |
29 | 29 | ["tokenjuice", "/tools/tokenjuice"], |
30 | 30 | ]); |
| 31 | +const PLUGIN_REFERENCE_EXTRA_SECTIONS = new Map([ |
| 32 | +[ |
| 33 | +"whatsapp", |
| 34 | +`## Windows install note |
| 35 | + |
| 36 | +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: |
| 37 | + |
| 38 | +\`\`\`powershell |
| 39 | +winget install --id Git.Git -e |
| 40 | +\`\`\` |
| 41 | + |
| 42 | +Portable Git also works if its \`bin\` directory is on \`PATH\`.`, |
| 43 | +], |
| 44 | +]); |
31 | 45 | |
32 | 46 | function readJson(relativePath) { |
33 | 47 | return JSON.parse(fs.readFileSync(path.join(ROOT, relativePath), "utf8")); |
@@ -376,6 +390,7 @@ ${record.docs.map((link) => `- ${docLink(link)}`).join("\n")}`;
|
376 | 390 | |
377 | 391 | function renderReferencePage(record) { |
378 | 392 | const relatedDocs = renderRelatedDocs(record); |
| 393 | +const extraSections = PLUGIN_REFERENCE_EXTRA_SECTIONS.get(record.id); |
379 | 394 | return `--- |
380 | 395 | summary: "${record.description.replaceAll('"', '\\"')}" |
381 | 396 | read_when: |
@@ -394,7 +409,7 @@ ${record.description}
|
394 | 409 | |
395 | 410 | ## Surface |
396 | 411 | |
397 | | -${record.surface}${relatedDocs ? `\n\n${relatedDocs}` : ""} |
| 412 | +${record.surface}${extraSections ? `\n\n${extraSections}` : ""}${relatedDocs ? `\n\n${relatedDocs}` : ""} |
398 | 413 | `; |
399 | 414 | } |
400 | 415 | |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -1096,6 +1096,34 @@ describe("plugins cli install", () => {
|
1096 | 1096 | expect(runtimeErrors.at(-1)).toContain("npm install failed"); |
1097 | 1097 | }); |
1098 | 1098 | |
| 1099 | +it("adds a Git PATH hint when npm plugin dependency install cannot spawn git", async () => { |
| 1100 | +loadConfig.mockReturnValue({} as OpenClawConfig); |
| 1101 | +installPluginFromNpmSpec.mockResolvedValue({ |
| 1102 | +ok: false, |
| 1103 | +error: [ |
| 1104 | +"npm install failed:", |
| 1105 | +"npm error code ENOENT", |
| 1106 | +"npm error syscall spawn git", |
| 1107 | +"npm error path git", |
| 1108 | +].join("\n"), |
| 1109 | +}); |
| 1110 | +installHooksFromNpmSpec.mockResolvedValue({ |
| 1111 | +ok: false, |
| 1112 | +error: "package.json missing openclaw.hooks", |
| 1113 | +}); |
| 1114 | + |
| 1115 | +await expect( |
| 1116 | +runPluginsCommand(["plugins", "install", "npm:@openclaw/whatsapp"]), |
| 1117 | +).rejects.toThrow("__exit__:1"); |
| 1118 | + |
| 1119 | +expect(installPluginFromClawHub).not.toHaveBeenCalled(); |
| 1120 | +expect(runtimeErrors.at(-1)).toContain( |
| 1121 | +"one of this plugin's npm dependencies is fetched from a git URL", |
| 1122 | +); |
| 1123 | +expect(runtimeErrors.at(-1)).toContain("winget install --id Git.Git -e"); |
| 1124 | +expect(runtimeErrors.at(-1)).toContain("Also not a valid hook pack"); |
| 1125 | +}); |
| 1126 | + |
1099 | 1127 | it("does not resolve npm: prefixed bundled plugin ids through bundled installs", async () => { |
1100 | 1128 | loadConfig.mockReturnValue({ plugins: { load: { paths: [] } } } as OpenClawConfig); |
1101 | 1129 | installPluginFromNpmSpec.mockResolvedValue({ |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -176,16 +176,36 @@ export function formatPluginInstallWithHookFallbackError(
|
176 | 176 | pluginError: string, |
177 | 177 | hookError: string, |
178 | 178 | ): string { |
| 179 | +const formattedPluginError = formatPluginInstallAttemptError(pluginError); |
| 180 | +const formattedHookError = formatPluginInstallAttemptError(hookError); |
179 | 181 | if (/plugin already exists: .+ \(delete it first\)/.test(pluginError)) { |
180 | | -return `${pluginError}\nUse \`openclaw plugins update <id-or-npm-spec>\` to upgrade the tracked plugin, or rerun install with \`--force\` to replace it.`; |
| 182 | +return `${formattedPluginError}\nUse \`openclaw plugins update <id-or-npm-spec>\` to upgrade the tracked plugin, or rerun install with \`--force\` to replace it.`; |
181 | 183 | } |
182 | 184 | if ( |
183 | 185 | pluginError.startsWith("Invalid extensions directory:") || |
184 | 186 | pluginError === "Invalid path: must stay within extensions directory" |
185 | 187 | ) { |
186 | | -return pluginError; |
| 188 | +return formattedPluginError; |
187 | 189 | } |
188 | | -return `${pluginError}\nAlso not a valid hook pack: ${hookError}`; |
| 190 | +return `${formattedPluginError}\nAlso not a valid hook pack: ${formattedHookError}`; |
| 191 | +} |
| 192 | + |
| 193 | +const MISSING_GIT_FOR_NPM_DEPENDENCY_HINT = |
| 194 | +"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."; |
| 195 | + |
| 196 | +function formatPluginInstallAttemptError(error: string): string { |
| 197 | +if (!isMissingGitForNpmDependencyError(error)) { |
| 198 | +return error; |
| 199 | +} |
| 200 | +if (error.includes(MISSING_GIT_FOR_NPM_DEPENDENCY_HINT)) { |
| 201 | +return error; |
| 202 | +} |
| 203 | +return `${error}\n\n${MISSING_GIT_FOR_NPM_DEPENDENCY_HINT}`; |
| 204 | +} |
| 205 | + |
| 206 | +function isMissingGitForNpmDependencyError(error: string): boolean { |
| 207 | +const normalized = normalizeLowercaseStringOrEmpty(error); |
| 208 | +return /\bspawn\s+git\b/u.test(normalized) && /\benoent\b/u.test(normalized); |
189 | 209 | } |
190 | 210 | |
191 | 211 | export function logHookPackRestartHint(runtime: RuntimeEnv = defaultRuntime) { |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。