chore: Enable linting in `scripts`.

main
cpojer 2026-01-31 21:29:14 +09:00
parent 0ffc251704
commit 1838ab019b
No known key found for this signature in database
GPG Key ID: C29F94A3201118AF
21 changed files with 314 additions and 124 deletions

View File

@ -32,7 +32,6 @@
"Swabble/", "Swabble/",
"vendor/", "vendor/",
"extensions/", "extensions/",
"scripts/",
"ui/" "ui/"
] ]
} }

View File

@ -18,20 +18,28 @@ const DEFAULT_RUNS = 10;
function parseArg(flag: string): string | undefined { function parseArg(flag: string): string | undefined {
const idx = process.argv.indexOf(flag); const idx = process.argv.indexOf(flag);
if (idx === -1) return undefined; if (idx === -1) {
return undefined;
}
return process.argv[idx + 1]; return process.argv[idx + 1];
} }
function parseRuns(raw: string | undefined): number { function parseRuns(raw: string | undefined): number {
if (!raw) return DEFAULT_RUNS; if (!raw) {
return DEFAULT_RUNS;
}
const parsed = Number(raw); const parsed = Number(raw);
if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_RUNS; if (!Number.isFinite(parsed) || parsed <= 0) {
return DEFAULT_RUNS;
}
return Math.floor(parsed); return Math.floor(parsed);
} }
function median(values: number[]): number { function median(values: number[]): number {
if (values.length === 0) return 0; if (values.length === 0) {
const sorted = [...values].sort((a, b) => a - b); return 0;
}
const sorted = [...values].toSorted((a, b) => a - b);
const mid = Math.floor(sorted.length / 2); const mid = Math.floor(sorted.length / 2);
if (sorted.length % 2 === 0) { if (sorted.length % 2 === 0) {
return Math.round((sorted[mid - 1] + sorted[mid]) / 2); return Math.round((sorted[mid - 1] + sorted[mid]) / 2);

View File

@ -5,7 +5,6 @@ import { fileURLToPath } from "node:url";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const binDir = path.join(root, "bin"); const binDir = path.join(root, "bin");
const scriptPath = path.join(root, "scripts", "docs-list.js");
const binPath = path.join(binDir, "docs-list"); const binPath = path.join(binDir, "docs-list");
fs.mkdirSync(binDir, { recursive: true }); fs.mkdirSync(binDir, { recursive: true });

View File

@ -13,7 +13,9 @@ function parseArgs(argv: string[]): ParsedArgs {
const arg = argv[index]; const arg = argv[index];
if (arg === "--max") { if (arg === "--max") {
const next = argv[index + 1]; const next = argv[index + 1];
if (!next || Number.isNaN(Number(next))) throw new Error("Missing/invalid --max value"); if (!next || Number.isNaN(Number(next))) {
throw new Error("Missing/invalid --max value");
}
maxLines = Number(next); maxLines = Number(next);
index++; index++;
continue; continue;
@ -43,7 +45,9 @@ async function countLines(filePath: string): Promise<number> {
async function main() { async function main() {
// Makes `... | head` safe. // Makes `... | head` safe.
process.stdout.on("error", (error: NodeJS.ErrnoException) => { process.stdout.on("error", (error: NodeJS.ErrnoException) => {
if (error.code === "EPIPE") process.exit(0); if (error.code === "EPIPE") {
process.exit(0);
}
throw error; throw error;
}); });
@ -58,9 +62,11 @@ async function main() {
const offenders = results const offenders = results
.filter((result) => result.lines > maxLines) .filter((result) => result.lines > maxLines)
.sort((a, b) => b.lines - a.lines); .toSorted((a, b) => b.lines - a.lines);
if (!offenders.length) return; if (!offenders.length) {
return;
}
// Minimal, grep-friendly output. // Minimal, grep-friendly output.
for (const offender of offenders) { for (const offender of offenders) {

View File

@ -26,7 +26,9 @@ function copyHookMetadata() {
const entries = fs.readdirSync(srcBundled, { withFileTypes: true }); const entries = fs.readdirSync(srcBundled, { withFileTypes: true });
for (const entry of entries) { for (const entry of entries) {
if (!entry.isDirectory()) continue; if (!entry.isDirectory()) {
continue;
}
const hookName = entry.name; const hookName = entry.name;
const srcHookDir = path.join(srcBundled, hookName); const srcHookDir = path.join(srcBundled, hookName);

View File

@ -12,7 +12,9 @@ type Args = {
const mask = (value: string) => { const mask = (value: string) => {
const compact = value.trim(); const compact = value.trim();
if (!compact) return "missing"; if (!compact) {
return "missing";
}
const edge = compact.length >= 12 ? 6 : 4; const edge = compact.length >= 12 ? 6 : 4;
return `${compact.slice(0, edge)}${compact.slice(-edge)}`; return `${compact.slice(0, edge)}${compact.slice(-edge)}`;
}; };
@ -48,7 +50,9 @@ const loadAuthProfiles = (agentId: string) => {
process.env.CLAWDBOT_STATE_DIR?.trim() || process.env.CLAWDBOT_STATE_DIR?.trim() ||
path.join(os.homedir(), ".openclaw"); path.join(os.homedir(), ".openclaw");
const authPath = path.join(stateRoot, "agents", agentId, "agent", "auth-profiles.json"); const authPath = path.join(stateRoot, "agents", agentId, "agent", "auth-profiles.json");
if (!fs.existsSync(authPath)) throw new Error(`Missing: ${authPath}`); if (!fs.existsSync(authPath)) {
throw new Error(`Missing: ${authPath}`);
}
const store = JSON.parse(fs.readFileSync(authPath, "utf8")) as { const store = JSON.parse(fs.readFileSync(authPath, "utf8")) as {
profiles?: Record<string, { provider?: string; type?: string; token?: string; key?: string }>; profiles?: Record<string, { provider?: string; type?: string; token?: string; key?: string }>;
}; };
@ -61,9 +65,13 @@ const pickAnthropicTokens = (store: {
const profiles = store.profiles ?? {}; const profiles = store.profiles ?? {};
const found: Array<{ profileId: string; token: string }> = []; const found: Array<{ profileId: string; token: string }> = [];
for (const [id, cred] of Object.entries(profiles)) { for (const [id, cred] of Object.entries(profiles)) {
if (cred?.provider !== "anthropic") continue; if (cred?.provider !== "anthropic") {
continue;
}
const token = cred.type === "token" ? cred.token?.trim() : undefined; const token = cred.type === "token" ? cred.token?.trim() : undefined;
if (token) found.push({ profileId: id, token }); if (token) {
found.push({ profileId: id, token });
}
} }
return found; return found;
}; };
@ -87,7 +95,9 @@ const readClaudeCliKeychain = (): {
expiresAt?: number; expiresAt?: number;
scopes?: string[]; scopes?: string[];
} | null => { } | null => {
if (process.platform !== "darwin") return null; if (process.platform !== "darwin") {
return null;
}
try { try {
const raw = execFileSync( const raw = execFileSync(
"security", "security",
@ -96,9 +106,13 @@ const readClaudeCliKeychain = (): {
); );
const parsed = JSON.parse(raw.trim()) as Record<string, unknown>; const parsed = JSON.parse(raw.trim()) as Record<string, unknown>;
const oauth = parsed?.claudeAiOauth as Record<string, unknown> | undefined; const oauth = parsed?.claudeAiOauth as Record<string, unknown> | undefined;
if (!oauth || typeof oauth !== "object") return null; if (!oauth || typeof oauth !== "object") {
return null;
}
const accessToken = oauth.accessToken; const accessToken = oauth.accessToken;
if (typeof accessToken !== "string" || !accessToken.trim()) return null; if (typeof accessToken !== "string" || !accessToken.trim()) {
return null;
}
const expiresAt = typeof oauth.expiresAt === "number" ? oauth.expiresAt : undefined; const expiresAt = typeof oauth.expiresAt === "number" ? oauth.expiresAt : undefined;
const scopes = Array.isArray(oauth.scopes) const scopes = Array.isArray(oauth.scopes)
? oauth.scopes.filter((v): v is string => typeof v === "string") ? oauth.scopes.filter((v): v is string => typeof v === "string")
@ -110,10 +124,18 @@ const readClaudeCliKeychain = (): {
}; };
const chromeServiceNameForPath = (cookiePath: string): string => { const chromeServiceNameForPath = (cookiePath: string): string => {
if (cookiePath.includes("/Arc/")) return "Arc Safe Storage"; if (cookiePath.includes("/Arc/")) {
if (cookiePath.includes("/BraveSoftware/")) return "Brave Safe Storage"; return "Arc Safe Storage";
if (cookiePath.includes("/Microsoft Edge/")) return "Microsoft Edge Safe Storage"; }
if (cookiePath.includes("/Chromium/")) return "Chromium Safe Storage"; if (cookiePath.includes("/BraveSoftware/")) {
return "Brave Safe Storage";
}
if (cookiePath.includes("/Microsoft Edge/")) {
return "Microsoft Edge Safe Storage";
}
if (cookiePath.includes("/Chromium/")) {
return "Chromium Safe Storage";
}
return "Chrome Safe Storage"; return "Chrome Safe Storage";
}; };
@ -132,12 +154,18 @@ const readKeychainPassword = (service: string): string | null => {
}; };
const decryptChromeCookieValue = (encrypted: Buffer, service: string): string | null => { const decryptChromeCookieValue = (encrypted: Buffer, service: string): string | null => {
if (encrypted.length < 4) return null; if (encrypted.length < 4) {
return null;
}
const prefix = encrypted.subarray(0, 3).toString("utf8"); const prefix = encrypted.subarray(0, 3).toString("utf8");
if (prefix !== "v10" && prefix !== "v11") return null; if (prefix !== "v10" && prefix !== "v11") {
return null;
}
const password = readKeychainPassword(service); const password = readKeychainPassword(service);
if (!password) return null; if (!password) {
return null;
}
const key = crypto.pbkdf2Sync(password, "saltysalt", 1003, 16, "sha1"); const key = crypto.pbkdf2Sync(password, "saltysalt", 1003, 16, "sha1");
const iv = Buffer.alloc(16, 0x20); const iv = Buffer.alloc(16, 0x20);
@ -172,10 +200,16 @@ const queryChromeCookieDb = (cookieDb: string): string | null => {
], ],
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5000 }, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5000 },
).trim(); ).trim();
if (!out) return null; if (!out) {
if (out.startsWith("sk-ant-")) return out; return null;
}
if (out.startsWith("sk-ant-")) {
return out;
}
const hex = out.replace(/[^0-9A-Fa-f]/g, ""); const hex = out.replace(/[^0-9A-Fa-f]/g, "");
if (!hex) return null; if (!hex) {
return null;
}
const buf = Buffer.from(hex, "hex"); const buf = Buffer.from(hex, "hex");
const service = chromeServiceNameForPath(cookieDb); const service = chromeServiceNameForPath(cookieDb);
const decrypted = decryptChromeCookieValue(buf, service); const decrypted = decryptChromeCookieValue(buf, service);
@ -209,7 +243,9 @@ const queryFirefoxCookieDb = (cookieDb: string): string | null => {
}; };
const findClaudeSessionKey = (): { sessionKey: string; source: string } | null => { const findClaudeSessionKey = (): { sessionKey: string; source: string } | null => {
if (process.platform !== "darwin") return null; if (process.platform !== "darwin") {
return null;
}
const firefoxRoot = path.join( const firefoxRoot = path.join(
os.homedir(), os.homedir(),
@ -221,9 +257,13 @@ const findClaudeSessionKey = (): { sessionKey: string; source: string } | null =
if (fs.existsSync(firefoxRoot)) { if (fs.existsSync(firefoxRoot)) {
for (const entry of fs.readdirSync(firefoxRoot)) { for (const entry of fs.readdirSync(firefoxRoot)) {
const db = path.join(firefoxRoot, entry, "cookies.sqlite"); const db = path.join(firefoxRoot, entry, "cookies.sqlite");
if (!fs.existsSync(db)) continue; if (!fs.existsSync(db)) {
continue;
}
const value = queryFirefoxCookieDb(db); const value = queryFirefoxCookieDb(db);
if (value) return { sessionKey: value, source: `firefox:${db}` }; if (value) {
return { sessionKey: value, source: `firefox:${db}` };
}
} }
} }
@ -236,15 +276,21 @@ const findClaudeSessionKey = (): { sessionKey: string; source: string } | null =
]; ];
for (const root of chromeCandidates) { for (const root of chromeCandidates) {
if (!fs.existsSync(root)) continue; if (!fs.existsSync(root)) {
continue;
}
const profiles = fs const profiles = fs
.readdirSync(root) .readdirSync(root)
.filter((name) => name === "Default" || name.startsWith("Profile ")); .filter((name) => name === "Default" || name.startsWith("Profile "));
for (const profile of profiles) { for (const profile of profiles) {
const db = path.join(root, profile, "Cookies"); const db = path.join(root, profile, "Cookies");
if (!fs.existsSync(db)) continue; if (!fs.existsSync(db)) {
continue;
}
const value = queryChromeCookieDb(db); const value = queryChromeCookieDb(db);
if (value) return { sessionKey: value, source: `chromium:${db}` }; if (value) {
return { sessionKey: value, source: `chromium:${db}` };
}
} }
} }

View File

@ -32,8 +32,14 @@ function compactStrings(values) {
if (value === null || value === undefined) { if (value === null || value === undefined) {
continue; continue;
} }
const normalized = String(value).trim(); const normalized =
if (normalized.length > 0) { typeof value === "string"
? value.trim()
: typeof value === "number" || typeof value === "boolean"
? String(value).trim()
: null;
if (normalized?.length > 0) {
result.push(normalized); result.push(normalized);
} }
} }
@ -62,7 +68,7 @@ function walkMarkdownFiles(dir, base = dir) {
files.push(relative(base, fullPath)); files.push(relative(base, fullPath));
} }
} }
return files.sort((a, b) => a.localeCompare(b)); return files.toSorted((a, b) => a.localeCompare(b));
} }
/** /**

View File

@ -18,7 +18,9 @@ const userAgent =
const timeoutMs = 30_000; const timeoutMs = 30_000;
function truncate(value: string, max = 180): string { function truncate(value: string, max = 180): string {
if (!value) return ""; if (!value) {
return "";
}
return value.length > max ? `${value.slice(0, max)}` : value; return value.length > max ? `${value.slice(0, max)}` : value;
} }
@ -89,8 +91,12 @@ async function run() {
} }
console.log(`local: ${localStatus} len=${localText.length} title=${truncate(localTitle, 80)}`); console.log(`local: ${localStatus} len=${localText.length} title=${truncate(localTitle, 80)}`);
if (localError) console.log(`local error: ${localError}`); if (localError) {
if (localText) console.log(`local sample: ${truncate(localText)}`); console.log(`local error: ${localError}`);
}
if (localText) {
console.log(`local sample: ${truncate(localText)}`);
}
if (apiKey) { if (apiKey) {
try { try {
@ -111,8 +117,12 @@ async function run() {
80, 80,
)} status=${firecrawl.status ?? "n/a"}`, )} status=${firecrawl.status ?? "n/a"}`,
); );
if (firecrawl.warning) console.log(`firecrawl warning: ${firecrawl.warning}`); if (firecrawl.warning) {
if (firecrawl.text) console.log(`firecrawl sample: ${truncate(firecrawl.text)}`); console.log(`firecrawl warning: ${firecrawl.warning}`);
}
if (firecrawl.text) {
console.log(`firecrawl sample: ${truncate(firecrawl.text)}`);
}
} catch (error) { } catch (error) {
const message = error instanceof Error ? error.message : String(error); const message = error instanceof Error ? error.message : String(error);
console.log(`firecrawl: error ${message}`); console.log(`firecrawl: error ${message}`);

View File

@ -19,7 +19,9 @@ function runGitCommand(args, options = {}) {
} }
function splitNullDelimited(value) { function splitNullDelimited(value) {
if (!value) return []; if (!value) {
return [];
}
const text = String(value); const text = String(value);
return text.split("\0").filter(Boolean); return text.split("\0").filter(Boolean);
} }
@ -44,7 +46,9 @@ function findPartiallyStagedFiles(stagedFiles, unstagedFiles) {
} }
function filterOutPartialTargets(targets, partialTargets) { function filterOutPartialTargets(targets, partialTargets) {
if (partialTargets.length === 0) return targets; if (partialTargets.length === 0) {
return targets;
}
const partial = new Set(partialTargets.map(normalizeGitPath)); const partial = new Set(partialTargets.map(normalizeGitPath));
return targets.filter((filePath) => !partial.has(normalizeGitPath(filePath))); return targets.filter((filePath) => !partial.has(normalizeGitPath(filePath)));
} }
@ -66,7 +70,9 @@ function resolveOxfmtCommand(repoRoot) {
function getGitPaths(args, repoRoot) { function getGitPaths(args, repoRoot) {
const result = runGitCommand(args, { cwd: repoRoot }); const result = runGitCommand(args, { cwd: repoRoot });
if (result.status !== 0) return []; if (result.status !== 0) {
return [];
}
return splitNullDelimited(result.stdout ?? ""); return splitNullDelimited(result.stdout ?? "");
} }
@ -79,7 +85,9 @@ function formatFiles(repoRoot, oxfmt, files) {
} }
function stageFiles(repoRoot, files) { function stageFiles(repoRoot, files) {
if (files.length === 0) return true; if (files.length === 0) {
return true;
}
const result = runGitCommand(["add", "--", ...files], { cwd: repoRoot, stdio: "inherit" }); const result = runGitCommand(["add", "--", ...files], { cwd: repoRoot, stdio: "inherit" });
return result.status === 0; return result.status === 0;
} }
@ -91,7 +99,9 @@ function main() {
repoRoot, repoRoot,
); );
const targets = filterOxfmtTargets(staged); const targets = filterOxfmtTargets(staged);
if (targets.length === 0) return; if (targets.length === 0) {
return;
}
const unstaged = getGitPaths(["diff", "--name-only", "-z"], repoRoot); const unstaged = getGitPaths(["diff", "--name-only", "-z"], repoRoot);
const partial = findPartiallyStagedFiles(targets, unstaged); const partial = findPartiallyStagedFiles(targets, unstaged);
@ -104,7 +114,9 @@ function main() {
} }
const filteredTargets = filterOutPartialTargets(targets, partial); const filteredTargets = filterOutPartialTargets(targets, partial);
if (filteredTargets.length === 0) return; if (filteredTargets.length === 0) {
return;
}
const oxfmt = resolveOxfmtCommand(repoRoot); const oxfmt = resolveOxfmtCommand(repoRoot);
if (!oxfmt) { if (!oxfmt) {

View File

@ -10,10 +10,18 @@ function detectPackageManager(ua = process.env.npm_config_user_agent ?? "") {
// - "npm/10.9.4 node/v22.12.0 linux x64" // - "npm/10.9.4 node/v22.12.0 linux x64"
// - "bun/1.2.2" // - "bun/1.2.2"
const normalized = String(ua).trim(); const normalized = String(ua).trim();
if (normalized.startsWith("pnpm/")) return "pnpm"; if (normalized.startsWith("pnpm/")) {
if (normalized.startsWith("bun/")) return "bun"; return "pnpm";
if (normalized.startsWith("npm/")) return "npm"; }
if (normalized.startsWith("yarn/")) return "yarn"; if (normalized.startsWith("bun/")) {
return "bun";
}
if (normalized.startsWith("npm/")) {
return "npm";
}
if (normalized.startsWith("yarn/")) {
return "yarn";
}
return "unknown"; return "unknown";
} }
@ -28,38 +36,42 @@ function getRepoRoot() {
} }
function ensureExecutable(targetPath) { function ensureExecutable(targetPath) {
if (process.platform === "win32") return; if (process.platform === "win32") {
if (!fs.existsSync(targetPath)) return; return;
}
if (!fs.existsSync(targetPath)) {
return;
}
try { try {
const mode = fs.statSync(targetPath).mode & 0o777; const mode = fs.statSync(targetPath).mode & 0o777;
if (mode & 0o100) return; if (mode & 0o100) {
return;
}
fs.chmodSync(targetPath, 0o755); fs.chmodSync(targetPath, 0o755);
} catch (err) { } catch (err) {
console.warn(`[postinstall] chmod failed: ${err}`); console.warn(`[postinstall] chmod failed: ${err}`);
} }
} }
function hasGit(repoRoot) {
const result = spawnSync("git", ["--version"], {
cwd: repoRoot,
stdio: "ignore",
});
return result.status === 0;
}
function extractPackageName(key) { function extractPackageName(key) {
if (key.startsWith("@")) { if (key.startsWith("@")) {
const idx = key.indexOf("@", 1); const idx = key.indexOf("@", 1);
if (idx === -1) return key; if (idx === -1) {
return key;
}
return key.slice(0, idx); return key.slice(0, idx);
} }
const idx = key.lastIndexOf("@"); const idx = key.lastIndexOf("@");
if (idx <= 0) return key; if (idx <= 0) {
return key;
}
return key.slice(0, idx); return key.slice(0, idx);
} }
function stripPrefix(p) { function stripPrefix(p) {
if (p.startsWith("a/") || p.startsWith("b/")) return p.slice(2); if (p.startsWith("a/") || p.startsWith("b/")) {
return p.slice(2);
}
return p; return p;
} }
@ -89,7 +101,9 @@ function parsePatch(patchText) {
i += 1; i += 1;
// Skip index line(s) // Skip index line(s)
while (i < lines.length && lines[i].startsWith("index ")) i += 1; while (i < lines.length && lines[i].startsWith("index ")) {
i += 1;
}
if (i < lines.length && lines[i].startsWith("--- ")) { if (i < lines.length && lines[i].startsWith("--- ")) {
file.oldPath = stripPrefix(lines[i].slice(4).trim()); file.oldPath = stripPrefix(lines[i].slice(4).trim());
@ -103,7 +117,9 @@ function parsePatch(patchText) {
while (i < lines.length && lines[i].startsWith("@@")) { while (i < lines.length && lines[i].startsWith("@@")) {
const header = lines[i]; const header = lines[i];
const match = /^@@\s+(-\d+(?:,\d+)?)\s+(\+\d+(?:,\d+)?)\s+@@/.exec(header); const match = /^@@\s+(-\d+(?:,\d+)?)\s+(\+\d+(?:,\d+)?)\s+@@/.exec(header);
if (!match) throw new Error(`invalid hunk header: ${header}`); if (!match) {
throw new Error(`invalid hunk header: ${header}`);
}
const oldRange = parseRange(match[1]); const oldRange = parseRange(match[1]);
const newRange = parseRange(match[2]); const newRange = parseRange(match[2]);
i += 1; i += 1;
@ -111,7 +127,9 @@ function parsePatch(patchText) {
const hunkLines = []; const hunkLines = [];
while (i < lines.length) { while (i < lines.length) {
const line = lines[i]; const line = lines[i];
if (line.startsWith("@@") || line.startsWith("diff --git ")) break; if (line.startsWith("@@") || line.startsWith("diff --git ")) {
break;
}
if (line === "") { if (line === "") {
i += 1; i += 1;
continue; continue;
@ -148,7 +166,9 @@ function readFileLines(targetPath) {
const raw = fs.readFileSync(targetPath, "utf-8"); const raw = fs.readFileSync(targetPath, "utf-8");
const hasTrailingNewline = raw.endsWith("\n"); const hasTrailingNewline = raw.endsWith("\n");
const parts = raw.split("\n"); const parts = raw.split("\n");
if (hasTrailingNewline) parts.pop(); if (hasTrailingNewline) {
parts.pop();
}
return { lines: parts, hasTrailingNewline }; return { lines: parts, hasTrailingNewline };
} }
@ -235,7 +255,9 @@ function applyPatchSet({ patchText, targetDir }) {
resolvedTarget = fs.realpathSync(resolvedTarget); resolvedTarget = fs.realpathSync(resolvedTarget);
const files = parsePatch(patchText); const files = parsePatch(patchText);
if (files.length === 0) return; if (files.length === 0) {
return;
}
for (const filePatch of files) { for (const filePatch of files) {
applyPatchToFile(resolvedTarget, filePatch); applyPatchToFile(resolvedTarget, filePatch);
@ -253,14 +275,20 @@ function applyPatchFile({ patchPath, targetDir }) {
function trySetupCompletion(repoRoot) { function trySetupCompletion(repoRoot) {
// Skip in CI or if explicitly disabled // Skip in CI or if explicitly disabled
if (process.env.CI || process.env.OPENCLAW_SKIP_COMPLETION_SETUP) return; if (process.env.CI || process.env.OPENCLAW_SKIP_COMPLETION_SETUP) {
return;
}
const binPath = path.join(repoRoot, "openclaw.mjs"); const binPath = path.join(repoRoot, "openclaw.mjs");
if (!fs.existsSync(binPath)) return; if (!fs.existsSync(binPath)) {
return;
}
// In development, dist might not exist yet during postinstall // In development, dist might not exist yet during postinstall
const distEntry = path.join(repoRoot, "dist", "index.js"); const distEntry = path.join(repoRoot, "dist", "index.js");
if (!fs.existsSync(distEntry)) return; if (!fs.existsSync(distEntry)) {
return;
}
try { try {
// Run with OPENCLAW_SKIP_POSTINSTALL to avoid any weird recursion, // Run with OPENCLAW_SKIP_POSTINSTALL to avoid any weird recursion,
@ -270,7 +298,7 @@ function trySetupCompletion(repoRoot) {
stdio: "inherit", stdio: "inherit",
env: { ...process.env, OPENCLAW_SKIP_POSTINSTALL: "1" }, env: { ...process.env, OPENCLAW_SKIP_POSTINSTALL: "1" },
}); });
} catch (err) { } catch {
// Ignore errors to not break install // Ignore errors to not break install
} }
} }
@ -294,9 +322,13 @@ function main() {
// Bun does not support pnpm.patchedDependencies. Apply these patch files to // Bun does not support pnpm.patchedDependencies. Apply these patch files to
// node_modules packages as a best-effort compatibility layer. // node_modules packages as a best-effort compatibility layer.
for (const [key, relPatchPath] of Object.entries(patched)) { for (const [key, relPatchPath] of Object.entries(patched)) {
if (typeof relPatchPath !== "string" || !relPatchPath.trim()) continue; if (typeof relPatchPath !== "string" || !relPatchPath.trim()) {
continue;
}
const pkgName = extractPackageName(String(key)); const pkgName = extractPackageName(String(key));
if (!pkgName) continue; if (!pkgName) {
continue;
}
applyPatchFile({ applyPatchFile({
targetDir: path.join("node_modules", ...pkgName.split("/")), targetDir: path.join("node_modules", ...pkgName.split("/")),
patchPath: relPatchPath, patchPath: relPatchPath,

View File

@ -72,7 +72,9 @@ function camelCase(input: string) {
function safeName(name: string) { function safeName(name: string) {
const cc = camelCase(name.replace(/-/g, "_")); const cc = camelCase(name.replace(/-/g, "_"));
if (reserved.has(cc)) return `_${cc}`; if (reserved.has(cc)) {
return `_${cc}`;
}
return cc; return cc;
} }
@ -86,11 +88,15 @@ function swiftType(schema: JsonSchema, required: boolean): string {
const named = schemaNameByObject.get(schema as object); const named = schemaNameByObject.get(schema as object);
if (named) { if (named) {
base = named; base = named;
} else if (t === "string") base = "String"; } else if (t === "string") {
else if (t === "integer") base = "Int"; base = "String";
else if (t === "number") base = "Double"; } else if (t === "integer") {
else if (t === "boolean") base = "Bool"; base = "Int";
else if (t === "array") { } else if (t === "number") {
base = "Double";
} else if (t === "boolean") {
base = "Bool";
} else if (t === "array") {
base = `[${swiftType(schema.items ?? { type: "Any" }, true)}]`; base = `[${swiftType(schema.items ?? { type: "Any" }, true)}]`;
} else if (schema.enum) { } else if (schema.enum) {
base = "String"; base = "String";
@ -213,7 +219,9 @@ async function generate() {
// Value structs // Value structs
for (const [name, schema] of definitions) { for (const [name, schema] of definitions) {
if (name === "GatewayFrame") continue; if (name === "GatewayFrame") {
continue;
}
if (schema.type === "object") { if (schema.type === "object") {
parts.push(emitStruct(name, schema)); parts.push(emitStruct(name, schema));
} }

View File

@ -23,7 +23,9 @@ async function runFetch(url: string, readability: boolean) {
}, },
sandboxed: false, sandboxed: false,
}); });
if (!tool) throw new Error("web_fetch tool is disabled"); if (!tool) {
throw new Error("web_fetch tool is disabled");
}
const result = await tool.execute("test", { url, extractMode: "markdown" }); const result = await tool.execute("test", { url, extractMode: "markdown" });
return result.details as { return result.details as {
text?: string; text?: string;
@ -35,7 +37,9 @@ async function runFetch(url: string, readability: boolean) {
} }
function truncate(value: string, max = 160): string { function truncate(value: string, max = 160): string {
if (!value) return ""; if (!value) {
return "";
}
return value.length > max ? `${value.slice(0, max)}` : value; return value.length > max ? `${value.slice(0, max)}` : value;
} }
@ -50,7 +54,9 @@ async function run() {
80, 80,
)}`, )}`,
); );
if (readable.text) console.log(`readability sample: ${truncate(readable.text)}`); if (readable.text) {
console.log(`readability sample: ${truncate(readable.text)}`);
}
} }
} }

View File

@ -27,7 +27,9 @@ const statMtime = (filePath) => {
const isExcludedSource = (filePath) => { const isExcludedSource = (filePath) => {
const relativePath = path.relative(srcRoot, filePath); const relativePath = path.relative(srcRoot, filePath);
if (relativePath.startsWith("..")) return false; if (relativePath.startsWith("..")) {
return false;
}
return ( return (
relativePath.endsWith(".test.ts") || relativePath.endsWith(".test.ts") ||
relativePath.endsWith(".test.tsx") || relativePath.endsWith(".test.tsx") ||
@ -40,7 +42,9 @@ const findLatestMtime = (dirPath, shouldSkip) => {
const queue = [dirPath]; const queue = [dirPath];
while (queue.length > 0) { while (queue.length > 0) {
const current = queue.pop(); const current = queue.pop();
if (!current) continue; if (!current) {
continue;
}
let entries = []; let entries = [];
try { try {
entries = fs.readdirSync(current, { withFileTypes: true }); entries = fs.readdirSync(current, { withFileTypes: true });
@ -53,10 +57,16 @@ const findLatestMtime = (dirPath, shouldSkip) => {
queue.push(fullPath); queue.push(fullPath);
continue; continue;
} }
if (!entry.isFile()) continue; if (!entry.isFile()) {
if (shouldSkip?.(fullPath)) continue; continue;
}
if (shouldSkip?.(fullPath)) {
continue;
}
const mtime = statMtime(fullPath); const mtime = statMtime(fullPath);
if (mtime == null) continue; if (mtime == null) {
continue;
}
if (latest == null || mtime > latest) { if (latest == null || mtime > latest) {
latest = mtime; latest = mtime;
} }
@ -66,23 +76,35 @@ const findLatestMtime = (dirPath, shouldSkip) => {
}; };
const shouldBuild = () => { const shouldBuild = () => {
if (env.OPENCLAW_FORCE_BUILD === "1") return true; if (env.OPENCLAW_FORCE_BUILD === "1") {
return true;
}
const stampMtime = statMtime(buildStampPath); const stampMtime = statMtime(buildStampPath);
if (stampMtime == null) return true; if (stampMtime == null) {
if (statMtime(distEntry) == null) return true; return true;
}
if (statMtime(distEntry) == null) {
return true;
}
for (const filePath of configFiles) { for (const filePath of configFiles) {
const mtime = statMtime(filePath); const mtime = statMtime(filePath);
if (mtime != null && mtime > stampMtime) return true; if (mtime != null && mtime > stampMtime) {
return true;
}
} }
const srcMtime = findLatestMtime(srcRoot, isExcludedSource); const srcMtime = findLatestMtime(srcRoot, isExcludedSource);
if (srcMtime != null && srcMtime > stampMtime) return true; if (srcMtime != null && srcMtime > stampMtime) {
return true;
}
return false; return false;
}; };
const logRunner = (message) => { const logRunner = (message) => {
if (env.OPENCLAW_RUNNER_LOG === "0") return; if (env.OPENCLAW_RUNNER_LOG === "0") {
return;
}
process.stderr.write(`[openclaw] ${message}\n`); process.stderr.write(`[openclaw] ${message}\n`);
}; };

View File

@ -20,11 +20,17 @@ function runGitCommand(args, options = {}) {
} }
function ensureExecutable(targetPath) { function ensureExecutable(targetPath) {
if (process.platform === "win32") return; if (process.platform === "win32") {
if (!fs.existsSync(targetPath)) return; return;
}
if (!fs.existsSync(targetPath)) {
return;
}
try { try {
const mode = fs.statSync(targetPath).mode & 0o777; const mode = fs.statSync(targetPath).mode & 0o777;
if (mode & 0o100) return; if (mode & 0o100) {
return;
}
fs.chmodSync(targetPath, 0o755); fs.chmodSync(targetPath, 0o755);
} catch (err) { } catch (err) {
console.warn(`[setup-git-hooks] chmod failed: ${err}`); console.warn(`[setup-git-hooks] chmod failed: ${err}`);
@ -41,7 +47,9 @@ function isGitRepo({ repoRoot = getRepoRoot(), runGit = runGitCommand } = {}) {
cwd: repoRoot, cwd: repoRoot,
stdio: "pipe", stdio: "pipe",
}); });
if (result.status !== 0) return false; if (result.status !== 0) {
return false;
}
return String(result.stdout ?? "").trim() === "true"; return String(result.stdout ?? "").trim() === "true";
} }

View File

@ -44,7 +44,9 @@ function replaceBlockLines(
]; ];
const replacement = replacementLines.join("\n"); const replacement = replacementLines.join("\n");
if (!after) return `${before}${replacement}`; if (!after) {
return `${before}${replacement}`;
}
return `${before}${replacement}\n${after}`; return `${before}${replacement}\n${after}`;
} }

View File

@ -6,7 +6,6 @@ type PackageJson = {
version?: string; version?: string;
}; };
const root = resolve(".");
const rootPackagePath = resolve("package.json"); const rootPackagePath = resolve("package.json");
const rootPackage = JSON.parse(readFileSync(rootPackagePath, "utf8")) as PackageJson; const rootPackage = JSON.parse(readFileSync(rootPackagePath, "utf8")) as PackageJson;
const targetVersion = rootPackage.version; const targetVersion = rootPackage.version;
@ -25,9 +24,13 @@ const changelogged: string[] = [];
const skipped: string[] = []; const skipped: string[] = [];
function ensureChangelogEntry(changelogPath: string, version: string): boolean { function ensureChangelogEntry(changelogPath: string, version: string): boolean {
if (!existsSync(changelogPath)) return false; if (!existsSync(changelogPath)) {
return false;
}
const content = readFileSync(changelogPath, "utf8"); const content = readFileSync(changelogPath, "utf8");
if (content.includes(`## ${version}`)) return false; if (content.includes(`## ${version}`)) {
return false;
}
const entry = `## ${version}\n\n### Changes\n- Version alignment with core OpenClaw release numbers.\n\n`; const entry = `## ${version}\n\n### Changes\n- Version alignment with core OpenClaw release numbers.\n\n`;
if (content.startsWith("# Changelog\n\n")) { if (content.startsWith("# Changelog\n\n")) {
const next = content.replace("# Changelog\n\n", `# Changelog\n\n${entry}`); const next = content.replace("# Changelog\n\n", `# Changelog\n\n${entry}`);

View File

@ -74,11 +74,15 @@ const runOnce = (entry, extraArgs = []) =>
}); });
const run = async (entry) => { const run = async (entry) => {
if (shardCount <= 1) return runOnce(entry); if (shardCount <= 1) {
return runOnce(entry);
}
for (let shardIndex = 1; shardIndex <= shardCount; shardIndex += 1) { for (let shardIndex = 1; shardIndex <= shardCount; shardIndex += 1) {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
const code = await runOnce(entry, ["--shard", `${shardIndex}/${shardCount}`]); const code = await runOnce(entry, ["--shard", `${shardIndex}/${shardCount}`]);
if (code !== 0) return code; if (code !== 0) {
return code;
}
} }
return 0; return 0;
}; };

View File

@ -28,7 +28,9 @@ function which(cmd) {
for (const ext of extensions) { for (const ext of extensions) {
const candidate = path.join(entry, process.platform === "win32" ? `${cmd}${ext}` : cmd); const candidate = path.join(entry, process.platform === "win32" ? `${cmd}${ext}` : cmd);
try { try {
if (fs.existsSync(candidate)) return candidate; if (fs.existsSync(candidate)) {
return candidate;
}
} catch { } catch {
// ignore // ignore
} }
@ -42,7 +44,9 @@ function which(cmd) {
function resolveRunner() { function resolveRunner() {
const pnpm = which("pnpm"); const pnpm = which("pnpm");
if (pnpm) return { cmd: pnpm, kind: "pnpm" }; if (pnpm) {
return { cmd: pnpm, kind: "pnpm" };
}
return null; return null;
} }
@ -54,7 +58,9 @@ function run(cmd, args) {
shell: process.platform === "win32", shell: process.platform === "win32",
}); });
child.on("exit", (code, signal) => { child.on("exit", (code, signal) => {
if (signal) process.exit(1); if (signal) {
process.exit(1);
}
process.exit(code ?? 1); process.exit(code ?? 1);
}); });
} }
@ -66,8 +72,12 @@ function runSync(cmd, args, envOverride) {
env: envOverride ?? process.env, env: envOverride ?? process.env,
shell: process.platform === "win32", shell: process.platform === "win32",
}); });
if (result.signal) process.exit(1); if (result.signal) {
if ((result.status ?? 1) !== 0) process.exit(result.status ?? 1); process.exit(1);
}
if ((result.status ?? 1) !== 0) {
process.exit(result.status ?? 1);
}
} }
function depsInstalled(kind) { function depsInstalled(kind) {
@ -114,8 +124,9 @@ if (action !== "install" && !script) {
process.exit(2); process.exit(2);
} }
if (action === "install") run(runner.cmd, ["install", ...rest]); if (action === "install") {
else { run(runner.cmd, ["install", ...rest]);
} else {
if (!depsInstalled(action === "test" ? "test" : "build")) { if (!depsInstalled(action === "test" ? "test" : "build")) {
const installEnv = const installEnv =
action === "build" ? { ...process.env, NODE_ENV: "production" } : process.env; action === "build" ? { ...process.env, NODE_ENV: "production" } : process.env;

View File

@ -229,7 +229,7 @@ const lines: string[] = [];
for (let i = 0; i < entries.length; i += PER_LINE) { for (let i = 0; i < entries.length; i += PER_LINE) {
const chunk = entries.slice(i, i + PER_LINE); const chunk = entries.slice(i, i + PER_LINE);
const parts = chunk.map((entry) => { const parts = chunk.map((entry) => {
return `<a href=\"${entry.html_url}\"><img src=\"${entry.avatar_url}\" width=\"48\" height=\"48\" alt=\"${entry.display}\" title=\"${entry.display}\"/></a>`; return `<a href="${entry.html_url}"><img src="${entry.avatar_url}" width="48" height="48" alt="${entry.display}" title="${entry.display}"/></a>`;
}); });
lines.push(` ${parts.join(" ")}`); lines.push(` ${parts.join(" ")}`);
} }
@ -243,7 +243,7 @@ if (start === -1 || end === -1) {
throw new Error("README.md missing clawtributors block"); throw new Error("README.md missing clawtributors block");
} }
const next = `${readme.slice(0, start)}<p align=\"left\">\n${block}${readme.slice(end)}`; const next = `${readme.slice(0, start)}<p align="left">\n${block}${readme.slice(end)}`;
writeFileSync(readmePath, next); writeFileSync(readmePath, next);
console.log(`Updated README clawtributors: ${entries.length} entries`); console.log(`Updated README clawtributors: ${entries.length} entries`);
@ -419,7 +419,7 @@ function parseReadmeEntries(
} }
const block = content.slice(start, end); const block = content.slice(start, end);
const entries: Array<{ display: string; html_url: string; avatar_url: string }> = []; const entries: Array<{ display: string; html_url: string; avatar_url: string }> = [];
const linked = /<a href=\"([^\"]+)\"><img src=\"([^\"]+)\"[^>]*alt=\"([^\"]+)\"[^>]*>/g; const linked = /<a href="([^"]+)"><img src="([^"]+)"[^>]*alt="([^"]+)"[^>]*>/g;
for (const match of block.matchAll(linked)) { for (const match of block.matchAll(linked)) {
const [, href, src, alt] = match; const [, href, src, alt] = match;
if (!href || !src || !alt) { if (!href || !src || !alt) {
@ -427,7 +427,7 @@ function parseReadmeEntries(
} }
entries.push({ html_url: href, avatar_url: src, display: alt }); entries.push({ html_url: href, avatar_url: src, display: alt });
} }
const standalone = /<img src=\"([^\"]+)\"[^>]*alt=\"([^\"]+)\"[^>]*>/g; const standalone = /<img src="([^"]+)"[^>]*alt="([^"]+)"[^>]*>/g;
for (const match of block.matchAll(standalone)) { for (const match of block.matchAll(standalone)) {
const [, src, alt] = match; const [, src, alt] = match;
if (!src || !alt) { if (!src || !alt) {
@ -442,7 +442,7 @@ function parseReadmeEntries(
} }
function loginFromUrl(url: string): string | null { function loginFromUrl(url: string): string | null {
const match = /^https?:\/\/github\.com\/([^\/?#]+)/i.exec(url); const match = /^https?:\/\/github\.com\/([^/?#]+)/i.exec(url);
if (!match) { if (!match) {
return null; return null;
} }

View File

@ -19,7 +19,9 @@ const readPackageVersion = () => {
const resolveCommit = () => { const resolveCommit = () => {
const envCommit = process.env.GIT_COMMIT?.trim() || process.env.GIT_SHA?.trim(); const envCommit = process.env.GIT_COMMIT?.trim() || process.env.GIT_SHA?.trim();
if (envCommit) return envCommit; if (envCommit) {
return envCommit;
}
try { try {
return execSync("git rev-parse HEAD", { return execSync("git rev-parse HEAD", {
cwd: rootDir, cwd: rootDir,

View File

@ -13,9 +13,13 @@ type RunResult = {
function pickAnthropicEnv(): { type: "oauth" | "api"; value: string } | null { function pickAnthropicEnv(): { type: "oauth" | "api"; value: string } | null {
const oauth = process.env.ANTHROPIC_OAUTH_TOKEN?.trim(); const oauth = process.env.ANTHROPIC_OAUTH_TOKEN?.trim();
if (oauth) return { type: "oauth", value: oauth }; if (oauth) {
return { type: "oauth", value: oauth };
}
const api = process.env.ANTHROPIC_API_KEY?.trim(); const api = process.env.ANTHROPIC_API_KEY?.trim();
if (api) return { type: "api", value: api }; if (api) {
return { type: "api", value: api };
}
return null; return null;
} }