openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
220 lines (219 loc) • 11.2 kB
JavaScript
import { v as sortUniqueStrings } from "./string-normalization-DsCfAx8q.js";
import { t as escapeRegExp } from "./regexp-BZyMFTlj.js";
import { c as readJsonIfExists } from "./json-files-Bq1lIlQB.js";
import path from "node:path";
import fs from "node:fs/promises";
import pLimit from "p-limit";
/**
* Dist paths that contain local build metadata and should not be packaged as source.
* @internal Shared repository-script contract.
*/
const LOCAL_BUILD_METADATA_DIST_PATHS = Object.freeze([`dist/.buildstamp`, `dist/.runtime-postbuildstamp`]);
const LOCAL_BUILD_METADATA_DIST_PATH_SET = new Set(LOCAL_BUILD_METADATA_DIST_PATHS);
/** Return whether a dist-relative path is local build metadata. */
function isLocalBuildMetadataDistPath(relativePath) {
return LOCAL_BUILD_METADATA_DIST_PATH_SET.has(relativePath);
}
//#endregion
//#region src/infra/package-dist-inventory.ts
const PACKAGE_DIST_INVENTORY_RELATIVE_PATH = "dist/postinstall-inventory.json";
const PACKAGE_DIST_INVENTORY_SCAN_CONCURRENCY = 32;
const LEGACY_QA_CHANNEL_DIR = ["qa", "channel"].join("-");
const LEGACY_QA_LAB_DIR = ["qa", "lab"].join("-");
const OMITTED_QA_EXTENSION_PREFIXES = [`dist/extensions/${LEGACY_QA_CHANNEL_DIR}/`, `dist/extensions/${LEGACY_QA_LAB_DIR}/`];
const OMITTED_PRIVATE_QA_PLUGIN_SDK_PREFIXES = [`dist/plugin-sdk/extensions/${LEGACY_QA_CHANNEL_DIR}/`, `dist/plugin-sdk/extensions/${LEGACY_QA_LAB_DIR}/`];
const OMITTED_PRIVATE_QA_PLUGIN_SDK_FILES = /* @__PURE__ */ new Set([
`dist/plugin-sdk/${LEGACY_QA_CHANNEL_DIR}.d.ts`,
`dist/plugin-sdk/${LEGACY_QA_CHANNEL_DIR}.js`,
`dist/plugin-sdk/${LEGACY_QA_CHANNEL_DIR}-protocol.d.ts`,
`dist/plugin-sdk/${LEGACY_QA_CHANNEL_DIR}-protocol.js`,
`dist/plugin-sdk/${LEGACY_QA_LAB_DIR}.d.ts`,
`dist/plugin-sdk/${LEGACY_QA_LAB_DIR}.js`,
"dist/plugin-sdk/qa-runtime.d.ts",
"dist/plugin-sdk/qa-runtime.js",
`dist/plugin-sdk/src/plugin-sdk/${LEGACY_QA_CHANNEL_DIR}.d.ts`,
`dist/plugin-sdk/src/plugin-sdk/${LEGACY_QA_CHANNEL_DIR}-protocol.d.ts`,
`dist/plugin-sdk/src/plugin-sdk/${LEGACY_QA_LAB_DIR}.d.ts`,
"dist/plugin-sdk/src/plugin-sdk/qa-runtime.d.ts"
]);
const OMITTED_DEEP_PLUGIN_SDK_DECLARATION_PREFIX = "dist/plugin-sdk/src/";
const OMITTED_PRIVATE_QA_DIST_PREFIXES = ["dist/qa-runtime-"];
const OMITTED_PLUGIN_SDK_TEST_FILES = /* @__PURE__ */ new Set([
"dist/plugin-sdk/agent-runtime-test-contracts.d.ts",
"dist/plugin-sdk/agent-runtime-test-contracts.js",
"dist/plugin-sdk/channel-contract-testing.d.ts",
"dist/plugin-sdk/channel-contract-testing.js",
"dist/plugin-sdk/channel-target-testing.d.ts",
"dist/plugin-sdk/channel-target-testing.js",
"dist/plugin-sdk/channel-test-helpers.d.ts",
"dist/plugin-sdk/channel-test-helpers.js",
"dist/plugin-sdk/plugin-test-api.d.ts",
"dist/plugin-sdk/plugin-test-api.js",
"dist/plugin-sdk/plugin-test-contracts.d.ts",
"dist/plugin-sdk/plugin-test-contracts.js",
"dist/plugin-sdk/plugin-test-runtime.d.ts",
"dist/plugin-sdk/plugin-test-runtime.js",
"dist/plugin-sdk/provider-http-test-mocks.d.ts",
"dist/plugin-sdk/provider-http-test-mocks.js",
"dist/plugin-sdk/provider-test-contracts.d.ts",
"dist/plugin-sdk/provider-test-contracts.js",
"dist/plugin-sdk/test-env.d.ts",
"dist/plugin-sdk/test-env.js",
"dist/plugin-sdk/test-fixtures.d.ts",
"dist/plugin-sdk/test-fixtures.js",
"dist/plugin-sdk/test-live.d.ts",
"dist/plugin-sdk/test-live.js",
"dist/plugin-sdk/test-live-auth.d.ts",
"dist/plugin-sdk/test-live-auth.js",
"dist/plugin-sdk/test-media-generation.d.ts",
"dist/plugin-sdk/test-media-generation.js",
"dist/plugin-sdk/test-media-understanding.d.ts",
"dist/plugin-sdk/test-media-understanding.js",
"dist/plugin-sdk/test-node-mocks.d.ts",
"dist/plugin-sdk/test-node-mocks.js"
]);
const OMITTED_PLUGIN_SDK_TEST_PREFIXES = [
"dist/plugin-sdk/src/agents/test-helpers/",
"dist/plugin-sdk/src/plugin-sdk/test-helpers/",
"dist/plugin-sdk/src/test-helpers/",
"dist/plugin-sdk/src/test-utils/"
];
const OMITTED_DIST_SUBTREE_PATTERNS = [
/^dist\/extensions\/node_modules(?:\/|$)/u,
/^dist\/extensions\/[^/]+\/node_modules(?:\/|$)/u,
/^dist\/plugin-sdk\/src(?:\/|$)/u,
new RegExp(`^dist/plugin-sdk/extensions/${LEGACY_QA_CHANNEL_DIR}(?:/|$)`, "u"),
new RegExp(`^dist/plugin-sdk/extensions/${LEGACY_QA_LAB_DIR}(?:/|$)`, "u")
];
function normalizeRelativePath(value) {
return value.replace(/\\/g, "/");
}
function splitRelativePath(relativePath) {
return normalizeRelativePath(relativePath).split("/");
}
function isLegacyPluginDependencyDirPath(relativePath) {
const parts = splitRelativePath(relativePath);
if (parts[0]?.toLowerCase() !== "dist" || parts[1]?.toLowerCase() !== "extensions") return false;
if ((parts[2] ?? "").toLowerCase() === "node_modules") return true;
return (parts[3] ?? "").toLowerCase() === "node_modules";
}
function compilePackageFilesExclusionPattern(pattern) {
let source = "^";
for (let index = 0; index < pattern.length; index += 1) {
const char = pattern[index];
if (char === "*") {
if (pattern[index + 1] === "*") {
if (pattern[index + 2] === "/") {
source += "(?:[^/]+/)*";
index += 2;
} else {
source += ".*";
index += 1;
}
} else source += "[^/]*";
continue;
}
source += escapeRegExp(char ?? "");
}
source += "$";
return new RegExp(source, "u");
}
function collectPackageDistExclusionRules(rootPackageJson) {
if (!rootPackageJson || typeof rootPackageJson !== "object") return {
files: /* @__PURE__ */ new Set(),
prefixes: [],
patterns: []
};
const files = rootPackageJson.files;
if (!Array.isArray(files)) return {
files: /* @__PURE__ */ new Set(),
prefixes: [],
patterns: []
};
const excludedFiles = /* @__PURE__ */ new Set();
const excludedPrefixes = /* @__PURE__ */ new Set();
const excludedPatterns = [];
for (const entry of files) {
if (typeof entry !== "string") continue;
const normalized = normalizeRelativePath(entry);
const match = /^!dist\/extensions\/([^/]+)\/\*\*$/u.exec(normalized);
if (match?.[1]) {
excludedFiles.add(`dist/extensions/${match[1]}`);
excludedPrefixes.add(`dist/extensions/${match[1]}/`);
}
if (!normalized.startsWith("!dist/")) continue;
const excludedPath = normalized.slice(1);
if (excludedPath.endsWith("/**") && !excludedPath.slice(0, -3).includes("*")) excludedPrefixes.add(excludedPath.slice(0, -2));
else if (excludedPath.includes("*")) excludedPatterns.push(compilePackageFilesExclusionPattern(excludedPath));
else excludedFiles.add(excludedPath);
}
return {
files: excludedFiles,
prefixes: [...excludedPrefixes].toSorted((left, right) => left.localeCompare(right)),
patterns: excludedPatterns
};
}
function isOmittedPluginSdkTestPath(relativePath) {
return OMITTED_PLUGIN_SDK_TEST_FILES.has(relativePath) || OMITTED_PLUGIN_SDK_TEST_PREFIXES.some((prefix) => relativePath === prefix.slice(0, -1) || relativePath.startsWith(prefix));
}
async function collectPackageDistExclusionRulesForRoot(packageRoot) {
const packageJsonPath = path.join(packageRoot, "package.json");
return collectPackageDistExclusionRules(await readJsonIfExists(packageJsonPath));
}
function isPackageFilesExcludedDistPath(relativePath, exclusions) {
return exclusions.files.has(relativePath) || exclusions.prefixes.some((prefix) => relativePath.startsWith(prefix)) || exclusions.patterns.some((pattern) => pattern.test(relativePath));
}
function isPackagedDistPath(relativePath, rules) {
if (!relativePath.startsWith("dist/")) return false;
if (isPackageFilesExcludedDistPath(relativePath, rules)) return false;
if (isLegacyPluginDependencyDirPath(relativePath)) return false;
if (relativePath === "dist/postinstall-inventory.json") return false;
if (isLocalBuildMetadataDistPath(relativePath)) return false;
if (relativePath.endsWith(".map")) return false;
if (relativePath === "dist/plugin-sdk/.tsbuildinfo") return false;
if (isOmittedPluginSdkTestPath(relativePath)) return false;
if (relativePath.startsWith(OMITTED_DEEP_PLUGIN_SDK_DECLARATION_PREFIX)) return false;
if (OMITTED_PRIVATE_QA_PLUGIN_SDK_PREFIXES.some((prefix) => relativePath.startsWith(prefix)) || OMITTED_PRIVATE_QA_PLUGIN_SDK_FILES.has(relativePath) || OMITTED_PRIVATE_QA_DIST_PREFIXES.some((prefix) => relativePath.startsWith(prefix))) return false;
if (OMITTED_QA_EXTENSION_PREFIXES.some((prefix) => relativePath.startsWith(prefix))) return false;
return true;
}
function isOmittedDistSubtree(relativePath, rules) {
return isPackageFilesExcludedDistPath(relativePath, rules) || isPackageFilesExcludedDistPath(`${relativePath}/`, rules) || isLegacyPluginDependencyDirPath(relativePath) || isOmittedPluginSdkTestPath(relativePath) || OMITTED_DIST_SUBTREE_PATTERNS.some((pattern) => pattern.test(relativePath));
}
async function collectRelativeFiles(rootDir, baseDir, rules, fsLimit, onDirectory) {
const rootRelativePath = normalizeRelativePath(path.relative(baseDir, rootDir));
if (rootRelativePath && isOmittedDistSubtree(rootRelativePath, rules)) return [];
try {
const rootStats = await fsLimit(() => fs.lstat(rootDir));
if (!rootStats.isDirectory() || rootStats.isSymbolicLink()) throw new Error(`Unsafe package dist path: ${normalizeRelativePath(path.relative(baseDir, rootDir))}`);
await onDirectory?.(rootDir);
const entries = await fsLimit(() => fs.readdir(rootDir, { withFileTypes: true }));
return (await Promise.all(entries.map(async (entry) => {
const entryPath = path.join(rootDir, entry.name);
const relativePath = normalizeRelativePath(path.relative(baseDir, entryPath));
if (entry.isSymbolicLink()) throw new Error(`Unsafe package dist path: ${relativePath}`);
if (entry.isDirectory()) return await collectRelativeFiles(entryPath, baseDir, rules, fsLimit, onDirectory);
if (entry.isFile()) return isPackagedDistPath(relativePath, rules) ? [relativePath] : [];
return [];
}))).flat().toSorted((left, right) => left.localeCompare(right));
} catch (error) {
if (error.code === "ENOENT") return [];
throw error;
}
}
/** Collects package dist files that should be present after install/update publication. */
async function collectPackageDistInventory(packageRoot, options = {}) {
const rules = options.packageManifest === void 0 ? await collectPackageDistExclusionRulesForRoot(packageRoot) : collectPackageDistExclusionRules(options.packageManifest);
const fsLimit = pLimit(PACKAGE_DIST_INVENTORY_SCAN_CONCURRENCY);
return await collectRelativeFiles(path.join(packageRoot, "dist"), packageRoot, rules, fsLimit, options.onDirectory);
}
/** Reads an existing package dist inventory, returning null when the inventory is absent. */
async function readPackageDistInventoryIfPresent(packageRoot) {
const inventoryPath = path.join(packageRoot, PACKAGE_DIST_INVENTORY_RELATIVE_PATH);
const parsed = await readJsonIfExists(inventoryPath);
if (parsed === null) return null;
if (!Array.isArray(parsed) || parsed.some((entry) => typeof entry !== "string")) throw new Error(`Invalid package dist inventory at ${PACKAGE_DIST_INVENTORY_RELATIVE_PATH}`);
return sortUniqueStrings(parsed.map(normalizeRelativePath));
}
//#endregion
export { collectPackageDistInventory as n, readPackageDistInventoryIfPresent as r, PACKAGE_DIST_INVENTORY_RELATIVE_PATH as t };