Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions apps/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@
"react": "^19.0.0",
"react-dom": "^19.0.0",
"three": "^0.182.0",
"@elizaos/app-core": "2.0.0-alpha.537",
"@elizaos/app-hyperscape": "beta",
"@elizaos/shared": "beta"
"@elizaos/app-core": "alpha",
"@elizaos/app-hyperscape": "alpha",
"@elizaos/shared": "alpha"
},
"devDependencies": {
"@capacitor/cli": "8.3.1",
Expand Down
33 changes: 17 additions & 16 deletions apps/app/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,104 +393,104 @@
];
}

function resolveLocalElizaAppAliases(): Alias[] {
if (!hasLocalElizaWorkspace) return [];

function resolveExportTarget(value: unknown): string | null {
if (typeof value === "string") return value;
if (!value || typeof value !== "object" || Array.isArray(value)) {
return null;
}
const record = value as Record<string, unknown>;
for (const condition of ["source", "import", "default", "types"]) {
const target = record[condition];
if (typeof target === "string") return target;
}
return null;
}

function resolveRuntimeTarget(pkgDir: string, exportTarget: string): string {
if (exportTarget.startsWith("./dist/")) {
const sourceTarget = exportTarget
.replace(/^\.\/dist\//, "./src/")
.replace(/\.js$/, ".ts");
const sourcePath = resolveExistingUiSourceModule(
path.resolve(pkgDir, sourceTarget),
);
if (fs.existsSync(sourcePath)) {
return sourcePath;
}
}

const distPath = path.resolve(pkgDir, exportTarget);
if (fs.existsSync(distPath)) {
return distPath;
}

return distPath;
}

const aliases: Alias[] = [];
const packageRoots = [
{ dir: path.join(localElizaRoot, "plugins"), appPrefixOnly: true },
{ dir: path.join(localElizaRoot, "apps"), appPrefixOnly: false },
];

for (const packageRoot of packageRoots) {
if (!fs.existsSync(packageRoot.dir)) continue;

for (const entry of fs.readdirSync(packageRoot.dir, {
withFileTypes: true,
})) {
if (!entry.isDirectory()) continue;
if (packageRoot.appPrefixOnly && !entry.name.startsWith("app-")) {
continue;
}
const pkgPath = path.join(packageRoot.dir, entry.name, "package.json");
if (!fs.existsSync(pkgPath)) continue;
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")) as {
name?: string;
exports?: Record<string, unknown>;
};
const pkgName = pkg.name;
if (!pkgName) continue;
const pkgDir = path.dirname(pkgPath);

for (const [key, value] of Object.entries(pkg.exports || {})) {
const exportTarget = resolveExportTarget(value);
if (!exportTarget) continue;
const resolvedTarget = path.resolve(pkgDir, exportTarget);
// Only create an alias when the target file actually exists on disk.
// In a fresh local clone, dist/ may not be built yet. Skipping the
// alias lets the import fall through to the stub or npm package.
if (!fs.existsSync(resolvedTarget)) continue;
const aliasKey =
key === "." ? pkgName : `${pkgName}/${key.replace(/^\.\//, "")}`;
aliases.push({
find: new RegExp(`^${escapeRegExp(aliasKey)}$`),
replacement: resolveRuntimeTarget(pkgDir, exportTarget),
});
}

// Only add the src catch-all if at least one export target exists (i.e.
// the package has been built). Otherwise skip to avoid resolving src/
// imports for packages that are stubs or not yet compiled.
const hasSrcDir = fs.existsSync(path.join(pkgDir, "src"));
const hasBuiltExport = aliases.some((a) => {
const re = a.find;
return re instanceof RegExp && re.test(pkgName);
});
if (hasSrcDir && hasBuiltExport) {
aliases.push({
find: new RegExp(`^${escapeRegExp(pkgName)}/(.*)`),
replacement: path.resolve(pkgDir, "src/$1"),
});
}
}
}

return aliases;
}

Check notice on line 493 in apps/app/vite.config.ts

View check run for this annotation

codefactor.io / CodeFactor

apps/app/vite.config.ts#L396-L493

Complex Method

function resolveLocalSharedAliases(): Alias[] {
if (!hasLocalElizaWorkspace) return [];
Expand Down Expand Up @@ -572,226 +572,226 @@
];
}

function resolveLocalAppCoreAliases(): Alias[] {
// Map @elizaos/agent's root import to its real source so static named
// imports from compiled app-core code (e.g. account-pool.js's
// ACCOUNT_CREDENTIAL_PROVIDER_IDS) resolve. Server-only runtime branches
// tree-shake out for the renderer; the previous empty-module stub only
// had a default export and broke Rollup's static analysis.
const agentRootEntry = appCoreSrcRoot
? path.join(localElizaRoot, "packages/agent/src/index.ts")
: emptyNodeModuleEntry;
const packageAgnosticAliases: Alias[] = [
{
find: /^@elizaos\/agent$/,
replacement: agentRootEntry,
},
{
find: /^@elizaos\/core$/,
replacement: resolveElizaCoreBundlePath(),
},
{
find: /^@elizaos\/shared\/character-presets$/,
replacement: publishedSharedCharacterPresetsEntry,
},
// When a local eliza workspace is present and @elizaos/shared has been
// built, prefer the local dist over the bun-store published copy which
// may lag behind and miss exports added in the local workspace.
...resolveBuiltLocalSharedAliases(),
];

const appCorePkgPath = path.join(
localElizaRoot,
"packages/app-core/package.json",
);
if (!appCoreSrcRoot || !fs.existsSync(appCorePkgPath)) {
return packageAgnosticAliases;
}

const appCorePkgDir = path.dirname(appCorePkgPath);
const appCoreBrowserEntry = path.join(here, "src/app-core-browser-compat.js");
const appCorePkg = JSON.parse(fs.readFileSync(appCorePkgPath, "utf8")) as {
exports?: Record<string, unknown>;
};

const generatedAliases: Alias[] = [];

// Bare `@elizaos/app-core` resolves to `src/browser.ts` which now
// re-exports the full `dist/index.js` surface (so milady's `main.tsx`
// sees `DesktopOnboardingRuntime`, `AppProvider`, etc.) plus the
// hand-written browser shims on top. The server-only re-exports
// inside dist (account-pool, onboarding-routes, …) are kept
// renderer-safe by aliasing the underlying `@elizaos/agent` and
// `@elizaos/plugin-elizacloud` server packages to their browser-side
// stubs in `nativeModuleStubPlugin` + the empty-node-module bake-in.
generatedAliases.push({
find: /^@elizaos\/app-core$/,
replacement: appCoreBrowserEntry,
});

for (const [key, value] of Object.entries(appCorePkg.exports || {})) {
if (key === ".") continue; // handled by the explicit bare alias above
const aliasKey =
key === "."
? "@elizaos/app-core"
: `@elizaos/app-core/${key.replace(/^\.\//, "")}`;

// Resolve the string value, handling both plain strings and conditional exports.
const resolvedValue: string | null =
typeof value === "string"
? value
: typeof value === "object" && value !== null
? ((value as Record<string, string>).import ??
(value as Record<string, string>).default ??
null)
: null;

// CSS files in app-core exports point to dist paths (e.g. ./styles/styles.css).
// In Wave A these moved to @elizaos/ui. If the dist path doesn't exist locally,
// redirect to the UI source CSS so a fresh local clone builds without errors.
if (aliasKey.endsWith(".css") && resolvedValue) {
const distCssPath = path.resolve(appCorePkgDir, resolvedValue);
const baseName = path.basename(resolvedValue);
const uiCssPath = uiPkgRoot
? path.join(uiPkgRoot, "src/styles", baseName)
: null;
const resolvedPath = fs.existsSync(distCssPath)
? distCssPath
: uiCssPath && fs.existsSync(uiCssPath)
? uiCssPath
: null;
if (resolvedPath) {
generatedAliases.push({
find: new RegExp(`^${escapeRegExp(aliasKey)}$`),
replacement: resolvedPath,
});
}
continue;
}

const targetPath =
key === "."
? appCoreBrowserEntry
: resolvedValue
? path.resolve(appCorePkgDir, resolvedValue)
: null;
if (!targetPath) continue;

generatedAliases.push({
find: new RegExp(`^${escapeRegExp(aliasKey)}$`),
replacement: targetPath,
});
if (!aliasKey.endsWith(".js")) {
generatedAliases.push({
find: new RegExp(`^${escapeRegExp(aliasKey)}\\.js$`),
replacement: targetPath,
});
}
}

const uiSource = path.join(appCoreSrcRoot, "ui");
const uiPkgSrcRoot = uiPkgRoot ? path.join(uiPkgRoot, "src") : null;
const legacyAppCoreUiAliases: Alias[] = uiPkgSrcRoot
? [
{
find: /^@elizaos\/app-core$/,
replacement: appCoreBrowserEntry,
},
{
find: /^@elizaos\/app-core\.js$/,
replacement: appCoreBrowserEntry,
},
{
find: /^@elizaos\/app-core\/styles\/(.*)$/,
replacement: `${uiPkgSrcRoot}/styles/$1`,
},
{
find: /^@elizaos\/app-core\/api$/,
replacement: path.join(uiPkgSrcRoot, "api/index.ts"),
},
{
find: /^@elizaos\/app-core\/components\/character\/CharacterEditor$/,
replacement: path.join(
uiPkgSrcRoot,
"components/character/CharacterEditor.tsx",
),
},
{
find: /^@elizaos\/app-core\/components\/chat\/widgets\/types$/,
replacement: path.join(
uiPkgSrcRoot,
"components/chat/widgets/types.ts",
),
},
{
find: /^@elizaos\/app-core\/components\/(.+)$/,
replacement: `${uiPkgSrcRoot}/components/$1`,
customResolver: resolveExistingUiSourceModule,
},
{
find: /^@elizaos\/app-core\/platform$/,
replacement: path.join(uiPkgSrcRoot, "platform/index.ts"),
},
{
find: /^@elizaos\/app-core\/state\/(.+)$/,
replacement: `${uiPkgSrcRoot}/state/$1.ts`,
customResolver: resolveExistingUiSourceModule,
},
{
find: /^@elizaos\/app-core\/utils$/,
replacement: path.join(uiPkgSrcRoot, "utils/index.ts"),
},
{
find: /^@elizaos\/app-core\/utils\/(.+)$/,
replacement: `${uiPkgSrcRoot}/utils/$1.ts`,
customResolver: resolveExistingUiSourceModule,
},
{
find: /^@elizaos\/app-core\/widgets\/(.+)$/,
replacement: `${uiPkgSrcRoot}/widgets/$1.ts`,
customResolver: resolveExistingUiSourceModule,
},
]
: [];

// Wave A moved several components/types from @elizaos/app-core to @elizaos/ui.
// The catch-all maps to app-core/src/* which may not have them. Use a custom
// resolver to fall back to the ui source when the app-core path doesn't exist.
const uiComponentsSourceDir = uiPkgRoot ? path.join(uiPkgRoot, "src") : null;

function resolveAppCoreWithUiFallback(id: string): string {
if (fs.existsSync(id)) {
// A subpath like `@elizaos/app-core/api/auth` can map to a directory when
// eliza refactors a single file into a folder (api/auth.ts -> api/auth/index.ts).
// fs.existsSync() is true for directories, so resolve the directory's index
// instead of returning the dir itself (which vite tries to read -> EISDIR).
if (fs.statSync(id).isDirectory()) {
for (const idx of [`${id}/index.ts`, `${id}/index.tsx`]) {
if (fs.existsSync(idx)) return idx;
}
} else {
return id;
}
}
const withTsx = id.endsWith(".tsx") ? id : `${id}.tsx`;
if (fs.existsSync(withTsx)) return withTsx;
const withTs = id.endsWith(".ts") ? id : `${id}.ts`;
if (fs.existsSync(withTs)) return withTs;
if (uiComponentsSourceDir && appCoreSrcRoot) {
const relativeToSrc = id.includes(`${appCoreSrcRoot}/`)
? id.slice(appCoreSrcRoot.length + 1)
: null;
if (relativeToSrc) {
const uiEquiv = path.join(uiComponentsSourceDir, relativeToSrc);
if (fs.existsSync(uiEquiv)) return uiEquiv;
const uiEquivTsx = `${uiEquiv}.tsx`;
if (fs.existsSync(uiEquivTsx)) return uiEquivTsx;
const uiEquivTs = `${uiEquiv}.ts`;
if (fs.existsSync(uiEquivTs)) return uiEquivTs;
}
}
return id;
}

Check notice on line 794 in apps/app/vite.config.ts

View check run for this annotation

codefactor.io / CodeFactor

apps/app/vite.config.ts#L762-L794

Complex Method

return [
...generatedAliases,
Expand Down Expand Up @@ -2091,6 +2091,7 @@
"readRequestBodyBuffer",
"readTriggerConfig",
"readTriggerRuns",
"registerJsRuntimeFactory",
"requestRestart",
"resolveAdvancedCapabilitiesEnabled",
"resolveAppHeroImage",
Expand Down Expand Up @@ -2237,12 +2238,14 @@
strippedId: string,
capacitorNativeScopeRe: RegExp,
): string {
const normalizedStrippedId = strippedId.replace(/\\/g, "/");
if (strippedId === "@elizaos/agent/config/plugin-auto-enable") {
return generateAgentPluginAutoEnableStub();
}
if (
strippedId === "@elizaos/agent" ||
strippedId.startsWith("@elizaos/agent/")
normalizedStrippedId.startsWith("@elizaos/agent/") ||
normalizedStrippedId.includes("/packages/agent/src/")
) {
return generateElizaAgentStub();
}
Expand Down Expand Up @@ -2300,19 +2303,7 @@
// The renderer never paints to a terminal; stub so the server-side OAuth
// QR helper that imports it doesn't blow up the browser bundle.
"qrcode-terminal",
// Server-only plugins statically imported from the @elizaos/agent runtime.
// Their exports maps nest browser/node conditional exports that Vite 6's
// commonjs--resolver cannot walk. Stubbing returns an empty Proxy virtual
// module so the browser bundle never tries to execute server-only code.
// plugin-local-inference creates a Node dns.Resolver at module load
// (mobileDnsResolver) — server-only side effect. Stub so the renderer
// never evaluates that initializer.
"@elizaos/plugin-local-inference",
"@elizaos/plugin-anthropic",
"@elizaos/plugin-pdf",
"@elizaos/plugin-sql",
"@elizaos/plugin-agent-skills",
"@elizaos/plugin-agent-orchestrator",
"@elizaos/skills",
// The agent runtime is server-only — it lives in the API child
// process, not in the renderer. app-core/dist code can leak agent
// imports (account-pool etc.); stub them so Rollup doesn't try to
Expand Down Expand Up @@ -2367,99 +2358,104 @@
return {
name: "native-module-stub",
enforce: "pre",
resolveId(id) {
const normalizedId = id.replace(/\\/g, "/");
if (normalizedId.includes("/packages/agent/src/")) {
return VIRTUAL_PREFIX + id;
}
// Server-only `@elizaos/agent` is aliased via packageAgnosticAliases
// to `elizaos-agent-browser-stub.ts`. The resolve.alias step runs
// AFTER `commonjs--resolver` in some rollup paths, which causes
// dist-side static-named-import scans to fail before the alias
// fires. Intercept it here with enforce:"pre" so Rollup gets the
// stub from the start.
if (id === "@elizaos/agent") {
return VIRTUAL_PREFIX + id;
}
// Plugin-elizacloud is server-only (cloud secrets, TTS routing).
// The renderer reaches it transitively through `dist/api/onboarding-routes.js`
// re-exports; stub the entire surface so static named-import scans pass.
if (id === "@elizaos/plugin-elizacloud") {
return appCoreSrcRoot
? path.join(
appCoreSrcRoot,
"platform/elizaos-plugin-elizacloud-browser-stub.ts",
)
: VIRTUAL_PREFIX + id;
}
// Some published/browser-side packages still deep-import this app-core
// compatibility module even though the local app-core export map does
// not list it. Intercept before commonjs--resolver validates exports.
if (id === "@elizaos/app-core/ui-compat" && appCoreSrcRoot) {
return path.join(appCoreSrcRoot, "ui-compat.ts");
}
// Intercept ALL node: builtins before Vite externalizes them.
// The @elizaos/core node entry uses many Node APIs (crypto, fs, module,
// etc.) at the top level. Rather than stubbing each one individually,
// we return a Proxy-based virtual module for any node: import.
if (id.startsWith("node:")) return VIRTUAL_PREFIX + id;
// Also catch bare imports of Node builtins that get resolved differently
const nodeBuiltins = new Set([
"module",
"crypto",
"fs",
"path",
"os",
"url",
"util",
"stream",
"http",
"https",
"net",
"tls",
"zlib",
"child_process",
"worker_threads",
"perf_hooks",
"async_hooks",
"dns",
"dgram",
"readline",
"tty",
"cluster",
"v8",
"vm",
"assert",
"buffer",
"string_decoder",
"querystring",
"punycode",
]);
if (nodeBuiltins.has(id) || nodeBuiltins.has(id.split("/")[0]))
return `${VIRTUAL_PREFIX}node:${id}`;
const bare = id.startsWith("@")
? id.split("/").slice(0, 2).join("/")
: id.split("/")[0];
// Scoped: @node-llama-cpp/*
if (nativeScopeRe.test(id)) return VIRTUAL_PREFIX + id;
// Scoped: @napi-rs/keyring + platform binaries
if (napiRsKeyringScopeRe.test(id)) return VIRTUAL_PREFIX + id;
// Scoped: @snazzah/davey + platform binaries (Discord voice native bridge)
if (snazzahDaveyScopeRe.test(id)) return VIRTUAL_PREFIX + id;
// Compiled Node native addons (.node binaries) — e.g. zlib-sync's
// `build/Release/zlib_sync.node` pulled by @discordjs/ws. Browser
// can never load these; stub so Rollup doesn't emit bare imports.
if (/\.node(\?.*)?$/.test(id)) return VIRTUAL_PREFIX + id;
// Capacitor native plugins (@capacitor/* except @capacitor/core)
if (capacitorNativeScopeRe.test(id) && !IS_CAPACITOR_MOBILE_BUILD) {
return VIRTUAL_PREFIX + id;
}
// sharp's optional platform packages (@img/sharp-wasm32, etc.)
if (
id.startsWith("@img/sharp") ||
id.replace(/\\/g, "/").includes("/@img/sharp")
)
return VIRTUAL_PREFIX + id;
// Exact or sub-path match against native packages
if (bare.startsWith("@elizaos/plugin-")) return VIRTUAL_PREFIX + id;
if (nativePackages.has(bare)) return VIRTUAL_PREFIX + id;
return null;
},

Check notice on line 2458 in apps/app/vite.config.ts

View check run for this annotation

codefactor.io / CodeFactor

apps/app/vite.config.ts#L2361-L2458

Complex Method
load(id) {
if (!id.startsWith(VIRTUAL_PREFIX)) return null;

Expand Down Expand Up @@ -2550,14 +2546,19 @@
normalizeConnectorSource: "function(x){return x}",
registerAppRoutePluginLoader: "function(){}",
registerConnectorSourceAliases: "function(){}",
readWorkspaceFolderConfig: "function(){return null}",
resolveStateDir: "function(){return ''}",
resolveUserPath: "function(x){return x}",
};
// Check which are actually missing from the existing export block
const needed = Object.keys(missingExports).filter((n) => {
// Check if already exported (as named export or re-export alias)
// Check if already exported (direct declaration, named export, or
// re-export alias) before appending a browser stub.
const directExport = new RegExp(
`\\bexport\\s+(?:async\\s+)?(?:function|const|let|var|class)\\s+${n}\\b`,
);
if (directExport.test(patched)) return false;
const exportedAs = new RegExp(`\\b${n}\\b`);
// Search only in export{} blocks
const exportBlocks = patched.match(/export\s*\{[^}]+\}/g) || [];
return !exportBlocks.some((b) => exportedAs.test(b));
});
Expand Down
4 changes: 2 additions & 2 deletions apps/homepage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"test:e2e": "playwright test"
},
"dependencies": {
"@elizaos/app-core": "2.0.0-alpha.537",
"@elizaos/ui": "beta",
"@elizaos/app-core": "alpha",
"@elizaos/ui": "alpha",
"dompurify": "^3.4.0",
"mermaid": "^11.14.0",
"react": "^19.0.0",
Expand Down
Loading
Loading