From 6fb2d3d7d72773182986e41bf8b5d38c871447b1 Mon Sep 17 00:00:00 2001 From: cpojer Date: Tue, 3 Feb 2026 22:04:17 +0900 Subject: [PATCH] feat: remove slop. --- docs/install/index.md | 3 +- package.json | 63 +---- packages/clawdbot/package.json | 3 - packages/moltbot/package.json | 3 - scripts/docker/cleanup-smoke/Dockerfile | 1 - scripts/postinstall.js | 361 ------------------------ src/cli/update-cli.ts | 2 +- src/postinstall-patcher.test.ts | 173 ------------ 8 files changed, 7 insertions(+), 602 deletions(-) delete mode 100644 scripts/postinstall.js delete mode 100644 src/postinstall-patcher.test.ts diff --git a/docs/install/index.md b/docs/install/index.md index ad08da5c09..4ee9f12cd8 100644 --- a/docs/install/index.md +++ b/docs/install/index.md @@ -79,10 +79,9 @@ Or with pnpm: ```bash pnpm add -g openclaw@latest pnpm approve-builds -g # approve openclaw, node-llama-cpp, sharp, etc. -pnpm add -g openclaw@latest # re-run to execute postinstall scripts ``` -pnpm requires explicit approval for packages with build scripts. After the first install shows the "Ignored build scripts" warning, run `pnpm approve-builds -g` and select the listed packages, then re-run the install so postinstall scripts execute. +pnpm requires explicit approval for packages with build scripts. After the first install shows the "Ignored build scripts" warning, run `pnpm approve-builds -g` and select the listed packages. Then: diff --git a/package.json b/package.json index 2a16435304..7e385e0c9d 100644 --- a/package.json +++ b/package.json @@ -9,68 +9,16 @@ "openclaw": "openclaw.mjs" }, "files": [ + "assets/", "CHANGELOG.md", + "dist/", + "docs/", + "extensions/", "LICENSE", "openclaw.mjs", "README-header.png", "README.md", - "assets/**", - "dist/*.js", - "dist/*.json", - "docs/**", - "extensions/**", - "git-hooks/**", - "patches/**", - "scripts/format-staged.js", - "scripts/postinstall.js", - "scripts/setup-git-hooks.js", - "skills/**", - "dist/acp/**", - "dist/agents/**", - "dist/auto-reply/**", - "dist/browser/**", - "dist/canvas-host/**", - "dist/channels/**", - "dist/cli/**", - "dist/commands/**", - "dist/compat/**", - "dist/config/**", - "dist/control-ui/**", - "dist/cron/**", - "dist/daemon/**", - "dist/discord/**", - "dist/gateway/**", - "dist/hooks/**", - "dist/imessage/**", - "dist/infra/**", - "dist/line/**", - "dist/link-understanding/**", - "dist/logging/**", - "dist/macos/**", - "dist/markdown/**", - "dist/media-understanding/**", - "dist/media/**", - "dist/memory/**", - "dist/node-host/**", - "dist/pairing/**", - "dist/plugin-sdk/**", - "dist/plugins/**", - "dist/process/**", - "dist/providers/**", - "dist/routing/**", - "dist/security/**", - "dist/sessions/**", - "dist/shared/**", - "dist/signal/**", - "dist/slack/**", - "dist/telegram/**", - "dist/terminal/**", - "dist/tts/**", - "dist/tui/**", - "dist/utils/**", - "dist/web/**", - "dist/whatsapp/**", - "dist/wizard/**" + "skills/" ], "type": "module", "main": "dist/index.js", @@ -115,7 +63,6 @@ "openclaw": "node scripts/run-node.mjs", "openclaw:rpc": "node scripts/run-node.mjs agent --mode rpc --json", "plugins:sync": "node --import tsx scripts/sync-plugin-versions.ts", - "postinstall": "node scripts/postinstall.js", "prepack": "pnpm build && pnpm ui:build", "protocol:check": "pnpm protocol:gen && pnpm protocol:gen:swift && git diff --exit-code -- dist/protocol.schema.json apps/macos/Sources/OpenClawProtocol/GatewayModels.swift", "protocol:gen": "node --import tsx scripts/protocol-gen.ts", diff --git a/packages/clawdbot/package.json b/packages/clawdbot/package.json index c11cff167e..fb2536aefb 100644 --- a/packages/clawdbot/package.json +++ b/packages/clawdbot/package.json @@ -10,9 +10,6 @@ ".": "./index.js", "./cli-entry": "./bin/clawdbot.js" }, - "scripts": { - "postinstall": "node ./scripts/postinstall.js" - }, "dependencies": { "openclaw": "workspace:*" } diff --git a/packages/moltbot/package.json b/packages/moltbot/package.json index 8acf814887..827cdf743d 100644 --- a/packages/moltbot/package.json +++ b/packages/moltbot/package.json @@ -10,9 +10,6 @@ ".": "./index.js", "./cli-entry": "./bin/moltbot.js" }, - "scripts": { - "postinstall": "node ./scripts/postinstall.js" - }, "dependencies": { "openclaw": "workspace:*" } diff --git a/scripts/docker/cleanup-smoke/Dockerfile b/scripts/docker/cleanup-smoke/Dockerfile index 9aacf20ff0..73f1ac81e5 100644 --- a/scripts/docker/cleanup-smoke/Dockerfile +++ b/scripts/docker/cleanup-smoke/Dockerfile @@ -9,7 +9,6 @@ RUN apt-get update \ WORKDIR /repo COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ -COPY scripts/postinstall.js ./scripts/postinstall.js COPY scripts/setup-git-hooks.js ./scripts/setup-git-hooks.js RUN corepack enable \ && pnpm install --frozen-lockfile diff --git a/scripts/postinstall.js b/scripts/postinstall.js deleted file mode 100644 index c5daab4076..0000000000 --- a/scripts/postinstall.js +++ /dev/null @@ -1,361 +0,0 @@ -import { spawnSync } from "node:child_process"; -import fs from "node:fs"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import { setupGitHooks } from "./setup-git-hooks.js"; - -function detectPackageManager(ua = process.env.npm_config_user_agent ?? "") { - // Examples: - // - "pnpm/10.23.0 npm/? node/v22.21.1 darwin arm64" - // - "npm/10.9.4 node/v22.12.0 linux x64" - // - "bun/1.2.2" - const normalized = String(ua).trim(); - if (normalized.startsWith("pnpm/")) { - return "pnpm"; - } - if (normalized.startsWith("bun/")) { - return "bun"; - } - if (normalized.startsWith("npm/")) { - return "npm"; - } - if (normalized.startsWith("yarn/")) { - return "yarn"; - } - return "unknown"; -} - -function shouldApplyPnpmPatchedDependenciesFallback(pm = detectPackageManager()) { - // pnpm already applies pnpm.patchedDependencies itself; re-applying would fail. - return pm !== "pnpm"; -} - -function getRepoRoot() { - const here = path.dirname(fileURLToPath(import.meta.url)); - return path.resolve(here, ".."); -} - -function ensureExecutable(targetPath) { - if (process.platform === "win32") { - return; - } - if (!fs.existsSync(targetPath)) { - return; - } - try { - const mode = fs.statSync(targetPath).mode & 0o777; - if (mode & 0o100) { - return; - } - fs.chmodSync(targetPath, 0o755); - } catch (err) { - console.warn(`[postinstall] chmod failed: ${err}`); - } -} - -function extractPackageName(key) { - if (key.startsWith("@")) { - const idx = key.indexOf("@", 1); - if (idx === -1) { - return key; - } - return key.slice(0, idx); - } - const idx = key.lastIndexOf("@"); - if (idx <= 0) { - return key; - } - return key.slice(0, idx); -} - -function stripPrefix(p) { - if (p.startsWith("a/") || p.startsWith("b/")) { - return p.slice(2); - } - return p; -} - -function parseRange(segment) { - // segment: "-12,5" or "+7" - const [startRaw, countRaw] = segment.slice(1).split(","); - const start = Number.parseInt(startRaw, 10); - const count = countRaw ? Number.parseInt(countRaw, 10) : 1; - if (Number.isNaN(start) || Number.isNaN(count)) { - throw new Error(`invalid hunk range: ${segment}`); - } - return { start, count }; -} - -function parsePatch(patchText) { - const lines = patchText.split("\n"); - const files = []; - let i = 0; - - while (i < lines.length) { - if (!lines[i].startsWith("diff --git ")) { - i += 1; - continue; - } - - const file = { oldPath: null, newPath: null, hunks: [] }; - i += 1; - - // Skip index line(s) - while (i < lines.length && lines[i].startsWith("index ")) { - i += 1; - } - - if (i < lines.length && lines[i].startsWith("--- ")) { - file.oldPath = stripPrefix(lines[i].slice(4).trim()); - i += 1; - } - if (i < lines.length && lines[i].startsWith("+++ ")) { - file.newPath = stripPrefix(lines[i].slice(4).trim()); - i += 1; - } - - while (i < lines.length && lines[i].startsWith("@@")) { - const header = lines[i]; - const match = /^@@\s+(-\d+(?:,\d+)?)\s+(\+\d+(?:,\d+)?)\s+@@/.exec(header); - if (!match) { - throw new Error(`invalid hunk header: ${header}`); - } - const oldRange = parseRange(match[1]); - const newRange = parseRange(match[2]); - i += 1; - - const hunkLines = []; - while (i < lines.length) { - const line = lines[i]; - if (line.startsWith("@@") || line.startsWith("diff --git ")) { - break; - } - if (line === "") { - i += 1; - continue; - } - if (line.startsWith("\\ No newline at end of file")) { - i += 1; - continue; - } - hunkLines.push(line); - i += 1; - } - - file.hunks.push({ - oldStart: oldRange.start, - oldLines: oldRange.count, - newStart: newRange.start, - newLines: newRange.count, - lines: hunkLines, - }); - } - - if (file.newPath && file.hunks.length > 0) { - files.push(file); - } - } - - return files; -} - -function readFileLines(targetPath) { - if (!fs.existsSync(targetPath)) { - throw new Error(`target file missing: ${targetPath}`); - } - const raw = fs.readFileSync(targetPath, "utf-8"); - const hasTrailingNewline = raw.endsWith("\n"); - const parts = raw.split("\n"); - if (hasTrailingNewline) { - parts.pop(); - } - return { lines: parts, hasTrailingNewline }; -} - -function writeFileLines(targetPath, lines, hadTrailingNewline) { - const content = lines.join("\n") + (hadTrailingNewline ? "\n" : ""); - fs.writeFileSync(targetPath, content, "utf-8"); -} - -function applyHunk(lines, hunk, offset) { - let cursor = hunk.oldStart - 1 + offset; - const expected = []; - for (const raw of hunk.lines) { - const marker = raw[0]; - if (marker === " " || marker === "+") { - expected.push(raw.slice(1)); - } - } - if (cursor >= 0 && cursor + expected.length <= lines.length) { - let alreadyApplied = true; - for (let i = 0; i < expected.length; i += 1) { - if (lines[cursor + i] !== expected[i]) { - alreadyApplied = false; - break; - } - } - if (alreadyApplied) { - const delta = hunk.newLines - hunk.oldLines; - return offset + delta; - } - } - - for (const raw of hunk.lines) { - const marker = raw[0]; - const text = raw.slice(1); - if (marker === " ") { - if (lines[cursor] !== text) { - throw new Error( - `context mismatch at line ${cursor + 1}: expected "${text}", found "${lines[cursor] ?? ""}"`, - ); - } - cursor += 1; - } else if (marker === "-") { - if (lines[cursor] !== text) { - throw new Error( - `delete mismatch at line ${cursor + 1}: expected "${text}", found "${lines[cursor] ?? ""}"`, - ); - } - lines.splice(cursor, 1); - } else if (marker === "+") { - lines.splice(cursor, 0, text); - cursor += 1; - } else { - throw new Error(`unexpected hunk marker: ${marker}`); - } - } - - const delta = hunk.newLines - hunk.oldLines; - return offset + delta; -} - -function applyPatchToFile(targetDir, filePatch) { - if (filePatch.newPath === "/dev/null") { - // deletion not needed for our patches - return; - } - const relPath = stripPrefix(filePatch.newPath ?? filePatch.oldPath ?? ""); - const targetPath = path.join(targetDir, relPath); - const { lines, hasTrailingNewline } = readFileLines(targetPath); - - let offset = 0; - for (const hunk of filePatch.hunks) { - offset = applyHunk(lines, hunk, offset); - } - - writeFileLines(targetPath, lines, hasTrailingNewline); -} - -function applyPatchSet({ patchText, targetDir }) { - let resolvedTarget = path.resolve(targetDir); - if (!fs.existsSync(resolvedTarget) || !fs.statSync(resolvedTarget).isDirectory()) { - console.warn(`[postinstall] skip missing target: ${resolvedTarget}`); - return; - } - resolvedTarget = fs.realpathSync(resolvedTarget); - - const files = parsePatch(patchText); - if (files.length === 0) { - return; - } - - for (const filePatch of files) { - applyPatchToFile(resolvedTarget, filePatch); - } -} - -function applyPatchFile({ patchPath, targetDir }) { - const absPatchPath = path.resolve(patchPath); - if (!fs.existsSync(absPatchPath)) { - throw new Error(`missing patch: ${absPatchPath}`); - } - const patchText = fs.readFileSync(absPatchPath, "utf-8"); - applyPatchSet({ patchText, targetDir }); -} - -function trySetupCompletion(repoRoot) { - // Skip in CI or if explicitly disabled - if (process.env.CI || process.env.OPENCLAW_SKIP_COMPLETION_SETUP) { - return; - } - - const binPath = path.join(repoRoot, "openclaw.mjs"); - if (!fs.existsSync(binPath)) { - return; - } - - // In development, dist might not exist yet during postinstall - const distEntry = path.join(repoRoot, "dist", "index.js"); - if (!fs.existsSync(distEntry)) { - return; - } - - try { - // Run with OPENCLAW_SKIP_POSTINSTALL to avoid any weird recursion, - // though distinct from this script. - spawnSync(process.execPath, [binPath, "completion", "--install", "--yes", "--write-state"], { - cwd: repoRoot, - stdio: "inherit", - env: { ...process.env, OPENCLAW_SKIP_POSTINSTALL: "1" }, - }); - } catch { - // Ignore errors - } -} - -function main() { - const repoRoot = getRepoRoot(); - process.chdir(repoRoot); - - ensureExecutable(path.join(repoRoot, "dist", "entry.js")); - setupGitHooks({ repoRoot }); - trySetupCompletion(repoRoot); - - if (!shouldApplyPnpmPatchedDependenciesFallback()) { - return; - } - - const pkgPath = path.join(repoRoot, "package.json"); - const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")); - const patched = pkg?.pnpm?.patchedDependencies ?? {}; - - // Bun does not support pnpm.patchedDependencies. Apply these patch files to - // node_modules packages as a best-effort compatibility layer. - for (const [key, relPatchPath] of Object.entries(patched)) { - if (typeof relPatchPath !== "string" || !relPatchPath.trim()) { - continue; - } - const pkgName = extractPackageName(String(key)); - if (!pkgName) { - continue; - } - applyPatchFile({ - targetDir: path.join("node_modules", ...pkgName.split("/")), - patchPath: relPatchPath, - }); - } -} - -try { - const skip = - process.env.OPENCLAW_SKIP_POSTINSTALL === "1" || - process.env.CLAWDBOT_SKIP_POSTINSTALL === "1" || - process.env.VITEST === "true" || - process.env.NODE_ENV === "test"; - - if (!skip) { - main(); - } -} catch (err) { - console.error(String(err)); - process.exit(1); -} - -export { - applyPatchFile, - applyPatchSet, - applyPatchToFile, - detectPackageManager, - parsePatch, - shouldApplyPnpmPatchedDependenciesFallback, -}; diff --git a/src/cli/update-cli.ts b/src/cli/update-cli.ts index bf7c9bc4d2..805d45984a 100644 --- a/src/cli/update-cli.ts +++ b/src/cli/update-cli.ts @@ -208,7 +208,7 @@ async function tryWriteCompletionCache(root: string, jsonMode: boolean): Promise } const result = spawnSync(resolveNodeRunner(), [binPath, "completion", "--write-state"], { cwd: root, - env: { ...process.env, OPENCLAW_SKIP_POSTINSTALL: "1" }, + env: process.env, encoding: "utf-8", }); if (result.error) { diff --git a/src/postinstall-patcher.test.ts b/src/postinstall-patcher.test.ts deleted file mode 100644 index 2d3f6c1681..0000000000 --- a/src/postinstall-patcher.test.ts +++ /dev/null @@ -1,173 +0,0 @@ -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; -import { describe, expect, it } from "vitest"; -import { - applyPatchSet, - detectPackageManager, - shouldApplyPnpmPatchedDependenciesFallback, -} from "../scripts/postinstall.js"; - -function makeTempDir() { - return fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-patch-")); -} - -describe("postinstall patcher", () => { - it("detects package manager from user agent", () => { - expect(detectPackageManager("pnpm/10.0.0 npm/? node/v22.0.0")).toBe("pnpm"); - expect(detectPackageManager("npm/10.9.0 node/v22.0.0")).toBe("npm"); - expect(detectPackageManager("bun/1.2.2")).toBe("bun"); - expect(detectPackageManager("yarn/4.0.0 npm/? node/v22.0.0")).toBe("yarn"); - expect(detectPackageManager("")).toBe("unknown"); - }); - - it("skips pnpm.patchedDependencies fallback for pnpm", () => { - expect(shouldApplyPnpmPatchedDependenciesFallback("pnpm")).toBe(false); - expect(shouldApplyPnpmPatchedDependenciesFallback("npm")).toBe(true); - expect(shouldApplyPnpmPatchedDependenciesFallback("bun")).toBe(true); - expect(shouldApplyPnpmPatchedDependenciesFallback("unknown")).toBe(true); - }); - - it("applies a simple patch", () => { - const dir = makeTempDir(); - const target = path.join(dir, "lib"); - fs.mkdirSync(target); - - const filePath = path.join(target, "main.js"); - const original = `${[ - "var QRCode = require('./../vendor/QRCode'),", - " QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel'),", - ' black = "\\033[40m \\033[0m",', - ' white = "\\033[47m \\033[0m",', - " toCell = function (isBlack) {", - ].join("\n")}\n`; - fs.writeFileSync(filePath, original, "utf-8"); - - const patchText = `diff --git a/lib/main.js b/lib/main.js -index 0000000..1111111 100644 ---- a/lib/main.js -+++ b/lib/main.js -@@ -1,5 +1,5 @@ --var QRCode = require('./../vendor/QRCode'), -- QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel'), -+var QRCode = require('./../vendor/QRCode/index.js'), -+ QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel.js'), - black = "\\033[40m \\033[0m", - white = "\\033[47m \\033[0m", - toCell = function (isBlack) { -`; - - applyPatchSet({ patchText, targetDir: dir }); - - const updated = fs.readFileSync(filePath, "utf-8"); - expect(updated).toBe( - `${[ - "var QRCode = require('./../vendor/QRCode/index.js'),", - " QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel.js'),", - ' black = "\\033[40m \\033[0m",', - ' white = "\\033[47m \\033[0m",', - " toCell = function (isBlack) {", - ].join("\n")}\n`, - ); - - fs.rmSync(dir, { recursive: true, force: true }); - }); - - it("treats already-applied hunks as no-ops", () => { - const dir = makeTempDir(); - const target = path.join(dir, "lib"); - fs.mkdirSync(target); - - const filePath = path.join(target, "main.js"); - const original = `${[ - "var QRCode = require('./../vendor/QRCode'),", - " QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel'),", - ' black = "\\033[40m \\033[0m",', - ' white = "\\033[47m \\033[0m",', - " toCell = function (isBlack) {", - ].join("\n")}\n`; - fs.writeFileSync(filePath, original, "utf-8"); - - const patchText = `diff --git a/lib/main.js b/lib/main.js -index 0000000..1111111 100644 ---- a/lib/main.js -+++ b/lib/main.js -@@ -1,5 +1,5 @@ --var QRCode = require('./../vendor/QRCode'), -- QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel'), -+var QRCode = require('./../vendor/QRCode/index.js'), -+ QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel.js'), - black = "\\033[40m \\033[0m", - white = "\\033[47m \\033[0m", - toCell = function (isBlack) { -`; - - applyPatchSet({ patchText, targetDir: dir }); - applyPatchSet({ patchText, targetDir: dir }); - - const updated = fs.readFileSync(filePath, "utf-8"); - expect(updated).toBe( - `${[ - "var QRCode = require('./../vendor/QRCode/index.js'),", - " QRErrorCorrectLevel = require('./../vendor/QRCode/QRErrorCorrectLevel.js'),", - ' black = "\\033[40m \\033[0m",', - ' white = "\\033[47m \\033[0m",', - " toCell = function (isBlack) {", - ].join("\n")}\n`, - ); - - fs.rmSync(dir, { recursive: true, force: true }); - }); - - it("handles multiple hunks with offsets", () => { - const dir = makeTempDir(); - const filePath = path.join(dir, "file.txt"); - fs.writeFileSync( - filePath, - `${["alpha", "beta", "gamma", "delta", "epsilon"].join("\n")}\n`, - "utf-8", - ); - - const patchText = `diff --git a/file.txt b/file.txt ---- a/file.txt -+++ b/file.txt -@@ -1,3 +1,4 @@ - alpha - beta -+beta2 - gamma -@@ -3,3 +4,4 @@ - gamma --delta -+DELTA - epsilon -+zeta -`; - - applyPatchSet({ patchText, targetDir: dir }); - - const updated = fs.readFileSync(filePath, "utf-8").trim().split("\n"); - expect(updated).toEqual(["alpha", "beta", "beta2", "gamma", "DELTA", "epsilon", "zeta"]); - - fs.rmSync(dir, { recursive: true, force: true }); - }); - - it("throws on context mismatch", () => { - const dir = makeTempDir(); - const filePath = path.join(dir, "file.txt"); - fs.writeFileSync(filePath, "hello\nworld\n", "utf-8"); - - const patchText = `diff --git a/file.txt b/file.txt ---- a/file.txt -+++ b/file.txt -@@ -1,2 +1,2 @@ --hola -+hi - world -`; - - expect(() => applyPatchSet({ patchText, targetDir: dir })).toThrow(); - - fs.rmSync(dir, { recursive: true, force: true }); - }); -});