mirror of
https://github.com/Heretek-AI/openclaw.git
synced 2026-07-19 21:53:30 -04:00
a9ae1a6778
Matrix Protocol: - docker-compose.matrix.yml: Dendrite homeserver + PostgreSQL + Nginx TLS - src/channels/plugins/matrix-channel.ts: OpenClaw plugin implementation - docs/matrix-triad-setup.md: Setup guide with auth scheme (@tm1-4:triad.local) MCP Server Integration: - docs/mcp-triad-integration.md: SearXNG, Playwright, GitHub MCP configs - docs/mcp-curiosity-mapping.md: Gap-to-capability mapping Node Sync Architecture: - src/services/node-sync-service.ts: WebSocket peer sync + presence detection - src/services/node-sync-service.test.ts: Unit tests - docs/node-sync-architecture.md: Architecture docs Triad Resilience: - scripts/triad-corruption-check.mjs: SQLite + log + config + git integrity - docs/triad-resilience.md: Recovery procedures - .secure/deployment-logs/README.md: Schema v2 - skills/triad-heartbeat/SKILL.md: Corruption check integration NPM Publish Workflow: - scripts/npm-publish.mjs: version, changelog, validate, publish, rollback - .github/workflows/npm-publish.yml: GitHub Actions with provenance - docs/npm-publish-guide.md: Complete documentation All deliverables tested in Docker before production.
153 lines
4.5 KiB
JavaScript
Executable File
153 lines
4.5 KiB
JavaScript
Executable File
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
import { removePathIfExists } from "./runtime-postbuild-shared.mjs";
|
|
|
|
function symlinkType() {
|
|
return process.platform === "win32" ? "junction" : "dir";
|
|
}
|
|
|
|
function relativeSymlinkTarget(sourcePath, targetPath) {
|
|
const relativeTarget = path.relative(path.dirname(targetPath), sourcePath);
|
|
return relativeTarget || ".";
|
|
}
|
|
|
|
function ensureSymlink(targetValue, targetPath, type) {
|
|
try {
|
|
fs.symlinkSync(targetValue, targetPath, type);
|
|
return;
|
|
} catch (error) {
|
|
if (error?.code !== "EEXIST") {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
try {
|
|
if (fs.lstatSync(targetPath).isSymbolicLink() && fs.readlinkSync(targetPath) === targetValue) {
|
|
return;
|
|
}
|
|
} catch {
|
|
// Fall through and recreate the target when inspection fails.
|
|
}
|
|
|
|
removePathIfExists(targetPath);
|
|
fs.symlinkSync(targetValue, targetPath, type);
|
|
}
|
|
|
|
function symlinkPath(sourcePath, targetPath, type) {
|
|
ensureSymlink(relativeSymlinkTarget(sourcePath, targetPath), targetPath, type);
|
|
}
|
|
|
|
function shouldWrapRuntimeJsFile(sourcePath) {
|
|
return path.extname(sourcePath) === ".js";
|
|
}
|
|
|
|
function shouldCopyRuntimeFile(sourcePath) {
|
|
const relativePath = sourcePath.replace(/\\/g, "/");
|
|
return (
|
|
relativePath.endsWith("/package.json") ||
|
|
relativePath.endsWith("/openclaw.plugin.json") ||
|
|
relativePath.endsWith("/.codex-plugin/plugin.json") ||
|
|
relativePath.endsWith("/.claude-plugin/plugin.json") ||
|
|
relativePath.endsWith("/.cursor-plugin/plugin.json")
|
|
);
|
|
}
|
|
|
|
function writeRuntimeModuleWrapper(sourcePath, targetPath) {
|
|
const specifier = relativeSymlinkTarget(sourcePath, targetPath).replace(/\\/g, "/");
|
|
const normalizedSpecifier = specifier.startsWith(".") ? specifier : `./${specifier}`;
|
|
fs.writeFileSync(
|
|
targetPath,
|
|
[
|
|
`export * from ${JSON.stringify(normalizedSpecifier)};`,
|
|
`import * as module from ${JSON.stringify(normalizedSpecifier)};`,
|
|
"export default module.default;",
|
|
"",
|
|
].join("\n"),
|
|
"utf8",
|
|
);
|
|
}
|
|
|
|
function stagePluginRuntimeOverlay(sourceDir, targetDir) {
|
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
|
|
for (const dirent of fs.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
if (dirent.name === "node_modules") {
|
|
continue;
|
|
}
|
|
|
|
const sourcePath = path.join(sourceDir, dirent.name);
|
|
const targetPath = path.join(targetDir, dirent.name);
|
|
|
|
if (dirent.isDirectory()) {
|
|
stagePluginRuntimeOverlay(sourcePath, targetPath);
|
|
continue;
|
|
}
|
|
|
|
if (dirent.isSymbolicLink()) {
|
|
ensureSymlink(fs.readlinkSync(sourcePath), targetPath);
|
|
continue;
|
|
}
|
|
|
|
if (!dirent.isFile()) {
|
|
continue;
|
|
}
|
|
|
|
if (shouldWrapRuntimeJsFile(sourcePath)) {
|
|
writeRuntimeModuleWrapper(sourcePath, targetPath);
|
|
continue;
|
|
}
|
|
|
|
if (shouldCopyRuntimeFile(sourcePath)) {
|
|
fs.copyFileSync(sourcePath, targetPath);
|
|
continue;
|
|
}
|
|
|
|
symlinkPath(sourcePath, targetPath);
|
|
}
|
|
}
|
|
|
|
function linkPluginNodeModules(params) {
|
|
const runtimeNodeModulesDir = path.join(params.runtimePluginDir, "node_modules");
|
|
removePathIfExists(runtimeNodeModulesDir);
|
|
if (!fs.existsSync(params.sourcePluginNodeModulesDir)) {
|
|
return;
|
|
}
|
|
ensureSymlink(params.sourcePluginNodeModulesDir, runtimeNodeModulesDir, symlinkType());
|
|
}
|
|
|
|
export function stageBundledPluginRuntime(params = {}) {
|
|
const repoRoot = params.cwd ?? params.repoRoot ?? process.cwd();
|
|
const distRoot = path.join(repoRoot, "dist");
|
|
const runtimeRoot = path.join(repoRoot, "dist-runtime");
|
|
const distExtensionsRoot = path.join(distRoot, "extensions");
|
|
const runtimeExtensionsRoot = path.join(runtimeRoot, "extensions");
|
|
|
|
if (!fs.existsSync(distExtensionsRoot)) {
|
|
removePathIfExists(runtimeRoot);
|
|
return;
|
|
}
|
|
|
|
removePathIfExists(runtimeRoot);
|
|
fs.mkdirSync(runtimeExtensionsRoot, { recursive: true });
|
|
|
|
for (const dirent of fs.readdirSync(distExtensionsRoot, { withFileTypes: true })) {
|
|
if (!dirent.isDirectory()) {
|
|
continue;
|
|
}
|
|
const distPluginDir = path.join(distExtensionsRoot, dirent.name);
|
|
const runtimePluginDir = path.join(runtimeExtensionsRoot, dirent.name);
|
|
const distPluginNodeModulesDir = path.join(distPluginDir, "node_modules");
|
|
|
|
stagePluginRuntimeOverlay(distPluginDir, runtimePluginDir);
|
|
linkPluginNodeModules({
|
|
runtimePluginDir,
|
|
sourcePluginNodeModulesDir: distPluginNodeModulesDir,
|
|
});
|
|
}
|
|
}
|
|
|
|
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
|
stageBundledPluginRuntime();
|
|
}
|