Files
wehub-resource-sync 426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

871 lines
22 KiB
JavaScript

#!/usr/bin/env node
/** Supports app-core build, packaging, or development orchestration for setup upstreams mjs. */
import { spawn, spawnSync } from "node:child_process";
import {
existsSync,
mkdirSync,
readdirSync,
readFileSync,
realpathSync,
rmSync,
symlinkSync,
} from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const DEFAULT_REPO_ROOT = path.resolve(__dirname, "..");
export const LOCAL_UPSTREAM_SKIP_ENVS = ["ELIZA_SKIP_LOCAL_UPSTREAMS"];
export const LOCAL_UPSTREAM_FORCE_ENVS = ["ELIZA_FORCE_LOCAL_UPSTREAMS"];
export const ELIZA_GIT_URL = "https://github.com/elizaos/eliza.git";
export const ELIZA_BRANCH = "develop";
export const ELIZA_REQUIRED_FILES = ["package.json"];
export const ELIZA_BUILD_STEPS = [
{
// Fresh CI checkouts do not track generated protobuf types for @elizaos/core.
// Build the package once so src/types/generated exists before root typecheck/tests.
check: path.join(
"packages",
"typescript",
"src",
"types",
"generated",
"eliza",
"v1",
"agent_pb.ts",
),
cwd: path.join("packages", "typescript"),
args: ["run", "build"],
label: "@elizaos/core",
},
{
check: path.join("packages", "prompts", "dist", "typescript", "index.ts"),
cwd: path.join("packages", "prompts"),
args: ["run", "build:typescript"],
label: "@elizaos/prompts",
},
{
check: path.join("packages", "shared", "dist", "index.js"),
cwd: path.join("packages", "shared"),
args: ["run", "build"],
label: "@elizaos/shared",
alwaysRun: true,
},
{
check: path.join("packages", "skills", "dist", "index.js"),
cwd: path.join("packages", "skills"),
args: ["run", "build"],
label: "@elizaos/skills",
},
{
check: path.join("packages", "vault", "dist", "index.js"),
cwd: path.join("packages", "vault"),
args: ["run", "build"],
label: "@elizaos/vault",
},
{
check: path.join("packages", "security", "dist", "index.js"),
cwd: path.join("packages", "security"),
args: ["run", "build"],
label: "@elizaos/security",
},
{
check: path.join("packages", "plugin-remote-manifest", "dist", "index.js"),
cwd: path.join("packages", "plugin-remote-manifest"),
args: ["run", "build"],
label: "@elizaos/plugin-remote-manifest",
},
{
check: path.join("packages", "plugin-worker-runtime", "dist", "index.js"),
cwd: path.join("packages", "plugin-worker-runtime"),
args: ["run", "build"],
label: "@elizaos/plugin-worker-runtime",
},
{
// plugin-elizacloud imports types from @elizaos/cloud-sdk; without dist
// its tsup --dts pass fails with TS2307.
check: path.join("packages", "cloud", "sdk", "dist", "index.d.ts"),
cwd: path.join("packages", "cloud", "sdk"),
args: ["run", "build"],
label: "@elizaos/cloud-sdk",
},
{
// plugin-streaming imports isCloudConnected from @elizaos/cloud-routing;
// without dist its tsup --dts pass fails with TS2307.
check: path.join("packages", "cloud", "routing", "dist", "index.js"),
cwd: path.join("packages", "cloud", "routing"),
args: ["run", "build"],
label: "@elizaos/cloud-routing",
},
];
const PACKAGE_LINK_ROOTS = [
["node_modules"],
["apps", "app", "node_modules"],
["apps", "home", "node_modules"],
];
function toDisplayPath(targetPath) {
return path.normalize(targetPath);
}
function runCommand(command, args, { cwd, env = process.env, label } = {}) {
const printable = label ?? `${command} ${args.join(" ")}`;
return new Promise((resolve, reject) => {
const child = spawn(command, args, {
cwd,
env,
stdio: "inherit",
});
child.on("error", (error) => {
reject(
new Error(
`${printable} failed: ${error instanceof Error ? error.message : String(error)}`,
),
);
});
child.on("exit", (code, signal) => {
if (signal) {
reject(new Error(`${printable} exited due to signal ${signal}`));
return;
}
if ((code ?? 1) !== 0) {
reject(new Error(`${printable} exited with code ${code ?? 1}`));
return;
}
resolve();
});
});
}
function commandExists(command) {
const result = spawnSync(command, ["--version"], {
stdio: "ignore",
});
return result.status === 0;
}
function readPackageJson(packageDir) {
try {
return JSON.parse(
readFileSync(path.join(packageDir, "package.json"), "utf8"),
);
} catch {
return null;
}
}
function uniqueLinks(links) {
const deduped = new Map();
for (const link of links) {
deduped.set(link.linkPath, link);
}
return [...deduped.values()];
}
function getForceEnvKey(env = process.env) {
return LOCAL_UPSTREAM_FORCE_ENVS.find((key) => env[key] === "1") ?? null;
}
export function getRepoElizaRoot(repoRoot = DEFAULT_REPO_ROOT) {
return path.resolve(repoRoot, "eliza");
}
export function getRepoPluginsRoot(repoRoot = DEFAULT_REPO_ROOT) {
return path.resolve(repoRoot, "eliza", "plugins");
}
export function getElizaWorkspaceSkipReason(
repoRoot = DEFAULT_REPO_ROOT,
{ env = process.env, pathExists = existsSync } = {},
) {
const matchedSkipEnv =
LOCAL_UPSTREAM_SKIP_ENVS.find((key) => env[key] === "1") ?? null;
if (matchedSkipEnv) {
return `${matchedSkipEnv}=1`;
}
const devWorkspaceMarkers = [
path.join(repoRoot, ".git"),
path.join(repoRoot, "tsconfig.json"),
path.join(repoRoot, "apps", "app", "vite.config.ts"),
];
const isDevCheckout = devWorkspaceMarkers.every((marker) =>
pathExists(marker),
);
if (!isDevCheckout && !getForceEnvKey(env)) {
return "non-development install";
}
return null;
}
export function shouldSetupElizaWorkspace(
repoRoot = DEFAULT_REPO_ROOT,
options,
) {
return getElizaWorkspaceSkipReason(repoRoot, options) === null;
}
export function hasRequiredElizaWorkspaceFiles(
elizaRoot,
{ pathExists = existsSync } = {},
) {
return ELIZA_REQUIRED_FILES.every((relativePath) =>
pathExists(path.join(elizaRoot, relativePath)),
);
}
export function hasInstalledElizaDependencies(
elizaRoot,
{ pathExists = existsSync } = {},
) {
return (
pathExists(path.join(elizaRoot, "node_modules", ".bun")) &&
pathExists(path.join(elizaRoot, "node_modules", ".bin"))
);
}
function getPackageLinkEntries(repoRoot, packageName, targetPath) {
if (typeof packageName !== "string" || packageName.length === 0) {
return [];
}
const packageSegments = packageName.startsWith("@")
? packageName.split("/").filter(Boolean)
: [packageName];
if (
packageSegments.length === 0 ||
(packageName.startsWith("@") && packageSegments.length !== 2)
) {
return [];
}
return PACKAGE_LINK_ROOTS.map((segments) => ({
linkPath: path.join(repoRoot, ...segments, ...packageSegments),
targetPath,
}));
}
function discoverElizaPackageDirs(elizaRoot) {
const packageDirs = [];
for (const parentDir of ["packages", "plugins"]) {
const searchRoot = path.join(elizaRoot, parentDir);
if (!existsSync(searchRoot)) {
continue;
}
let entries = [];
try {
entries = readdirSync(searchRoot, { withFileTypes: true });
} catch {
continue;
}
for (const entry of entries) {
if (!entry.isDirectory() && !entry.isSymbolicLink()) {
continue;
}
const packageDir = path.join(searchRoot, entry.name);
const packageJson = readPackageJson(packageDir);
if (packageJson?.name?.startsWith("@elizaos/")) {
packageDirs.push(packageDir);
}
}
}
return packageDirs;
}
function discoverPluginPackageDirs(pluginsRoot) {
if (!existsSync(pluginsRoot)) {
return [];
}
const packageDirs = [];
let entries = [];
try {
entries = readdirSync(pluginsRoot, { withFileTypes: true });
} catch {
return packageDirs;
}
for (const entry of entries) {
if (!entry.isDirectory() && !entry.isSymbolicLink()) {
continue;
}
const repoDir = path.join(pluginsRoot, entry.name);
const tsDir = path.join(repoDir, "typescript");
const tsPackage = readPackageJson(tsDir);
if (tsPackage?.name?.startsWith("@elizaos/")) {
packageDirs.push(tsDir);
continue;
}
const rootPackage = readPackageJson(repoDir);
const rootName = rootPackage?.name;
const shouldLinkRoot =
typeof rootName === "string" &&
rootName.startsWith("@") &&
!rootName.endsWith("-root");
if (shouldLinkRoot) {
packageDirs.push(repoDir);
}
}
return packageDirs;
}
export function getElizaPackageLinks(
repoRoot = DEFAULT_REPO_ROOT,
elizaRoot = getRepoElizaRoot(repoRoot),
) {
const links = [];
for (const packageDir of discoverElizaPackageDirs(elizaRoot)) {
const packageJson = readPackageJson(packageDir);
links.push(
...getPackageLinkEntries(repoRoot, packageJson?.name, packageDir),
);
}
return uniqueLinks(links);
}
export function getPluginPackageLinks(
repoRoot = DEFAULT_REPO_ROOT,
pluginsRoot = getRepoPluginsRoot(repoRoot),
) {
const links = [];
for (const packageDir of discoverPluginPackageDirs(pluginsRoot)) {
const packageJson = readPackageJson(packageDir);
links.push(
...getPackageLinkEntries(repoRoot, packageJson?.name, packageDir),
);
}
return uniqueLinks(links);
}
export function getUpstreamPackageLinks(
repoRoot = DEFAULT_REPO_ROOT,
{
elizaRoot = getRepoElizaRoot(repoRoot),
pluginsRoot = getRepoPluginsRoot(repoRoot),
} = {},
) {
const combinedByTarget = new Map();
for (const link of getElizaPackageLinks(repoRoot, elizaRoot)) {
combinedByTarget.set(link.linkPath, link);
}
for (const link of getPluginPackageLinks(repoRoot, pluginsRoot)) {
combinedByTarget.set(link.linkPath, link);
}
return [...combinedByTarget.values()];
}
export function isPackageLinkCurrent(linkPath, targetPath) {
if (!existsSync(linkPath) || !existsSync(targetPath)) {
return false;
}
try {
return realpathSync(linkPath) === realpathSync(targetPath);
} catch {
return false;
}
}
function createLink(linkPath, targetPath, kind = "dir") {
if (isPackageLinkCurrent(linkPath, targetPath)) {
return false;
}
rmSync(linkPath, {
force: true,
recursive: true,
});
mkdirSync(path.dirname(linkPath), { recursive: true });
const linkTarget =
process.platform === "win32"
? targetPath
: path.relative(path.dirname(linkPath), targetPath) || ".";
const linkType =
process.platform === "win32"
? kind === "dir"
? "junction"
: "file"
: kind;
symlinkSync(linkTarget, linkPath, linkType);
return true;
}
export function createPackageLink(linkPath, targetPath) {
return createLink(linkPath, targetPath, "dir");
}
function createBinLink(linkPath, targetPath) {
return createLink(linkPath, targetPath, "file");
}
function getPackageBinEntries(packageJson) {
if (!packageJson) {
return [];
}
if (typeof packageJson.bin === "string") {
const packageBasename = packageJson.name?.split("/").pop();
if (!packageBasename) {
return [];
}
return [[packageBasename, packageJson.bin]];
}
if (!packageJson.bin || typeof packageJson.bin !== "object") {
return [];
}
return Object.entries(packageJson.bin).filter(
([binName, binPath]) =>
typeof binName === "string" &&
binName.length > 0 &&
typeof binPath === "string" &&
binPath.length > 0,
);
}
function ensurePackageBinLinks(
packageDir,
dependencyLinkPath,
dependencyPackageDir,
) {
let linkedBins = 0;
const dependencyPackageJson = readPackageJson(dependencyPackageDir);
const binEntries = getPackageBinEntries(dependencyPackageJson);
if (binEntries.length === 0) {
return linkedBins;
}
const packageBinDir = path.join(packageDir, "node_modules", ".bin");
mkdirSync(packageBinDir, { recursive: true });
for (const [binName, binRelativePath] of binEntries) {
const targetFile = path.join(dependencyPackageDir, binRelativePath);
if (!existsSync(targetFile)) {
continue;
}
const binLinkPath = path.join(packageBinDir, binName);
const binTargetPath = path.join(dependencyLinkPath, binRelativePath);
if (createBinLink(binLinkPath, binTargetPath)) {
linkedBins += 1;
}
}
return linkedBins;
}
function findInstalledPackageDir(
repoRoot,
packageName,
preferredVersion,
localTargetPath = null,
) {
const directPackagePath = path.join(
repoRoot,
"node_modules",
...packageName.split("/"),
);
try {
const resolved = realpathSync(directPackagePath);
const resolvedLocalTarget =
localTargetPath && existsSync(localTargetPath)
? realpathSync(localTargetPath)
: null;
if (existsSync(resolved) && resolved !== resolvedLocalTarget) {
return directPackagePath;
}
} catch {}
const bunCacheRoot = path.join(repoRoot, "node_modules", ".bun");
if (!existsSync(bunCacheRoot)) {
return null;
}
const packagePrefix = `${packageName.replace("/", "+")}@`;
const preferredPrefix =
preferredVersion === undefined
? null
: `${packageName.replace("/", "+")}@${preferredVersion}+`;
const matches = [];
for (const entry of readdirSync(bunCacheRoot, { withFileTypes: true })) {
if (!entry.isDirectory() || !entry.name.startsWith(packagePrefix)) {
continue;
}
const candidate = path.join(
bunCacheRoot,
entry.name,
"node_modules",
...packageName.split("/"),
);
if (!existsSync(candidate)) {
continue;
}
matches.push({
candidate,
preferred:
preferredPrefix !== null && entry.name.startsWith(preferredPrefix),
});
}
matches.sort(
(left, right) => Number(right.preferred) - Number(left.preferred),
);
return matches[0]?.candidate ?? null;
}
export function ensurePluginDependencyLinks(
repoRoot,
pluginsRoot = getRepoPluginsRoot(repoRoot),
) {
let linkedDependencies = 0;
for (const packageDir of discoverPluginPackageDirs(pluginsRoot)) {
const packageJson = readPackageJson(packageDir);
const packageName = packageJson?.name;
if (!packageName?.startsWith("@elizaos/")) {
continue;
}
rmSync(path.join(packageDir, "node_modules", ".bin"), {
force: true,
recursive: true,
});
const packageDependencies = {
...(packageJson.peerDependencies ?? {}),
...(packageJson.dependencies ?? {}),
...(packageJson.optionalDependencies ?? {}),
...(packageJson.devDependencies ?? {}),
};
const dependencyNames = Object.keys(packageDependencies);
if (dependencyNames.length === 0) {
continue;
}
for (const dependencyName of dependencyNames) {
const installedDependencyDir = findInstalledPackageDir(
repoRoot,
dependencyName,
);
if (!installedDependencyDir) {
continue;
}
const dependencyLinkPath = path.join(
packageDir,
"node_modules",
...dependencyName.split("/"),
);
if (createPackageLink(dependencyLinkPath, installedDependencyDir)) {
linkedDependencies += 1;
}
linkedDependencies += ensurePackageBinLinks(
packageDir,
dependencyLinkPath,
installedDependencyDir,
);
}
}
if (linkedDependencies > 0) {
console.log(
`[setup-upstreams] Linked ${linkedDependencies} plugin dependency ${linkedDependencies === 1 ? "entry" : "entries"}`,
);
}
return linkedDependencies;
}
export function getPublishedElizaPackageSpecs(repoRoot = DEFAULT_REPO_ROOT) {
const rootPackageJson = readPackageJson(repoRoot);
if (!rootPackageJson) {
return [];
}
const collectedSpecs = new Map();
for (const dependencyGroup of [
rootPackageJson.dependencies,
rootPackageJson.devDependencies,
rootPackageJson.optionalDependencies,
rootPackageJson.peerDependencies,
]) {
if (!dependencyGroup || typeof dependencyGroup !== "object") {
continue;
}
for (const [packageName, version] of Object.entries(dependencyGroup)) {
if (
!packageName.startsWith("@elizaos/") ||
typeof version !== "string" ||
version.startsWith("workspace:")
) {
continue;
}
collectedSpecs.set(packageName, version);
}
}
return [...collectedSpecs.entries()];
}
export function ensurePublishedElizaPackageLinks(repoRoot = DEFAULT_REPO_ROOT) {
let linkedEntries = 0;
for (const [packageName, preferredVersion] of getPublishedElizaPackageSpecs(
repoRoot,
)) {
const installedPackageDir = findInstalledPackageDir(
repoRoot,
packageName,
preferredVersion,
);
if (!installedPackageDir) {
continue;
}
for (const { linkPath, targetPath } of getPackageLinkEntries(
repoRoot,
packageName,
installedPackageDir,
)) {
if (path.resolve(linkPath) === path.resolve(targetPath)) {
continue;
}
if (createPackageLink(linkPath, targetPath)) {
linkedEntries += 1;
}
}
}
if (linkedEntries > 0) {
console.log(
`[setup-upstreams] Linked ${linkedEntries} published @elizaos package ${linkedEntries === 1 ? "entry" : "entries"}`,
);
}
return linkedEntries;
}
async function ensureRepoLocalEliza(repoRoot) {
const elizaRoot = getRepoElizaRoot(repoRoot);
if (hasRequiredElizaWorkspaceFiles(elizaRoot)) {
return elizaRoot;
}
if (existsSync(path.join(repoRoot, ".git"))) {
console.log("[setup-upstreams] Initializing tracked submodules");
try {
await runCommand(
"git",
["submodule", "update", "--init", "--recursive", "--", "eliza"],
{
cwd: repoRoot,
label: "git submodule update eliza",
},
);
} catch (error) {
if (existsSync(elizaRoot)) {
throw error;
}
console.warn(
`[setup-upstreams] Could not initialize eliza as a tracked submodule. Falling back to a direct clone (${error instanceof Error ? error.message : String(error)}).`,
);
}
}
if (!hasRequiredElizaWorkspaceFiles(elizaRoot) && !existsSync(elizaRoot)) {
console.log(
`[setup-upstreams] Cloning ${ELIZA_GIT_URL} (${ELIZA_BRANCH}) into ${toDisplayPath(elizaRoot)}`,
);
await runCommand(
"git",
[
"clone",
"--branch",
ELIZA_BRANCH,
"--single-branch",
ELIZA_GIT_URL,
elizaRoot,
],
{
cwd: repoRoot,
label: "git clone eliza",
},
);
}
if (!hasRequiredElizaWorkspaceFiles(elizaRoot)) {
throw new Error(
`Repo-local eliza workspace at ${toDisplayPath(elizaRoot)} is missing required files after setup.`,
);
}
return elizaRoot;
}
async function ensureElizaDependencies(elizaRoot) {
if (hasInstalledElizaDependencies(elizaRoot)) {
return;
}
console.log(
`[setup-upstreams] Installing eliza workspace dependencies in ${toDisplayPath(elizaRoot)}`,
);
await runCommand("bun", ["install"], {
cwd: elizaRoot,
label: "bun install (eliza)",
});
}
async function ensureElizaBuildOutputs(elizaRoot) {
for (const step of ELIZA_BUILD_STEPS) {
if (!step.alwaysRun && existsSync(path.join(elizaRoot, step.check))) {
continue;
}
console.log(`[setup-upstreams] Building ${step.label}`);
await runCommand("bun", step.args, {
cwd: path.join(elizaRoot, step.cwd),
label: `bun ${step.args.join(" ")} (${step.label})`,
});
}
}
export async function ensurePluginBuildOutputs(
pluginsRoot,
{ pathExists = existsSync, runCommandImpl = runCommand } = {},
) {
for (const packageDir of discoverPluginPackageDirs(pluginsRoot)) {
const packageJson = readPackageJson(packageDir);
if (!packageJson?.name?.startsWith("@elizaos/")) {
continue;
}
const hasBuildScript =
packageJson.scripts && typeof packageJson.scripts.build === "string";
if (!hasBuildScript || pathExists(path.join(packageDir, "dist"))) {
continue;
}
console.log(`[setup-upstreams] Building ${packageJson.name}`);
await runCommandImpl("bun", ["run", "build"], {
cwd: packageDir,
label: `bun run build (${packageJson.name})`,
});
}
}
export function linkUpstreamPackages(
repoRoot = DEFAULT_REPO_ROOT,
{
elizaRoot = getRepoElizaRoot(repoRoot),
pluginsRoot = getRepoPluginsRoot(repoRoot),
} = {},
) {
let updatedLinks = 0;
for (const { linkPath, targetPath } of getUpstreamPackageLinks(repoRoot, {
elizaRoot,
pluginsRoot,
})) {
if (createPackageLink(linkPath, targetPath)) {
updatedLinks += 1;
}
}
return updatedLinks;
}
export async function setupUpstreams(repoRoot = DEFAULT_REPO_ROOT) {
const skipReason = getElizaWorkspaceSkipReason(repoRoot);
if (skipReason) {
if (skipReason.endsWith("=1")) {
ensurePublishedElizaPackageLinks(repoRoot);
}
console.log(`[setup-upstreams] Skipping: ${skipReason}`);
return { skipped: true, reason: skipReason };
}
if (!commandExists("git")) {
throw new Error(
"git is required to initialize repo-local upstream sources",
);
}
if (!commandExists("bun")) {
throw new Error(
"bun is required to install and link repo-local upstream sources",
);
}
const elizaRoot = await ensureRepoLocalEliza(repoRoot);
await ensureElizaDependencies(elizaRoot);
await ensureElizaBuildOutputs(elizaRoot);
const pluginsRoot = getRepoPluginsRoot(repoRoot);
ensurePluginDependencyLinks(repoRoot, pluginsRoot);
await ensurePluginBuildOutputs(pluginsRoot);
const updatedLinks = linkUpstreamPackages(repoRoot, {
elizaRoot,
pluginsRoot,
});
if (updatedLinks === 0) {
console.log(
"[setup-upstreams] Repo-local @elizaos package links already up to date",
);
} else {
console.log(
`[setup-upstreams] Linked ${updatedLinks} repo-local @elizaos package ${updatedLinks === 1 ? "entry" : "entries"}`,
);
}
return {
skipped: false,
elizaRoot,
pluginsRoot: existsSync(pluginsRoot) ? pluginsRoot : null,
linkedEntries: updatedLinks,
};
}
const isMain =
process.argv[1] && path.resolve(process.argv[1]) === path.resolve(__filename);
if (isMain) {
setupUpstreams().catch((error) => {
console.error(
`[setup-upstreams] ${error instanceof Error ? error.message : String(error)}`,
);
process.exit(1);
});
}