life
Version:
Life.js is the first fullstack framework to build agentic web applications. It is minimal, extensible, and typesafe. Well, everything you love.
1,300 lines (1,287 loc) • 170 kB
JavaScript
import {
logLevelPriority,
telemetryBrowserScopesDefinition
} from "./chunk-5BLN2MK4.mjs";
import {
ProcessStats,
createTelemetryClient,
pipeConsoleToTelemetryClient,
telemetryNodeScopesDefinition
} from "./chunk-MLK5YGGI.mjs";
import {
TelemetryClient,
canon,
ns,
package_default,
stripAnsi,
telemetrySignalSchema
} from "./chunk-6CBODJTF.mjs";
import {
prepareAgentConfig
} from "./chunk-XNMZQAOR.mjs";
import {
AsyncQueue,
agentClientConfig,
deepClone
} from "./chunk-D2T23PCX.mjs";
import {
importServerBuild
} from "./chunk-CUJAJGIJ.mjs";
import {
failure,
isLifeError,
lifeError,
newId,
obfuscateLifeError,
success
} from "./chunk-ZHBK6UTM.mjs";
import {
__name
} from "./chunk-2D3UJWOA.mjs";
// cli/run.ts
import { Command as Command5 } from "commander";
// telemetry/helpers/formatting/terminal.ts
import path from "path";
import chalk2 from "chalk";
import esbuild from "esbuild";
import z from "zod";
// cli/utils/theme.ts
import chalk from "chalk";
var theme = {
orange: "#E77823",
gray: {
light: "#bbbbbb",
medium: "#888888",
dark: "#444444"
},
level: {
fatal: "red",
error: "red",
warn: "#FFA500",
info: "cyan",
debug: "gray"
}
};
var themeChalk = {
orange: chalk.hex(theme.orange),
gray: {
light: chalk.hex(theme.gray.light),
medium: chalk.hex(theme.gray.medium),
dark: chalk.hex(theme.gray.dark)
},
level: {
fatal: chalk.bgRed,
error: chalk.red,
info: chalk.cyan,
debug: chalk.gray,
warn: chalk.hex(theme.level.warn)
}
};
// telemetry/helpers/formatting/terminal.ts
var isEsbuildError = /* @__PURE__ */ __name((error) => {
if (error instanceof Error && "errors" in error) {
const errorsSchema = z.array(
z.object({
id: z.string().optional(),
pluginName: z.string().optional(),
text: z.string().optional()
})
);
const { success: success2 } = errorsSchema.safeParse(error.errors);
return success2;
}
return false;
}, "isEsbuildError");
function formatErrorForTerminal(error) {
let code = "";
let message = "";
let stack = "";
let after = "";
let processed = false;
if (isLifeError(error)) {
code = `LifeError (${chalk2.bold(error.code)})`;
message = error.message;
stack = error.stack ? error.stack.split("\n").slice(3).join("\n") : "";
if (error.cause) {
after += formatErrorForTerminal(error.cause);
const typedCause = error.cause;
if (error.code === "Unknown" && typedCause?.stack) stack = "";
}
processed = true;
} else if (error instanceof z.ZodError) {
code = "ZodError";
message = z.prettifyError(error);
stack = error.stack ?? "";
if (stack.includes(" at ")) {
stack = ` ${stack.split(" at ").slice(1).map((line) => ` at ${line}`).join("") ?? ""}`;
}
processed = true;
} else if (isEsbuildError(error)) {
const formatEsbuildMessage = /* @__PURE__ */ __name((msg) => esbuild.formatMessagesSync([msg], { kind: "error", color: true })?.[0]?.replace("\x1B[31m\u2718 \x1B[41;31m[\x1B[41;97mERROR\x1B[41;31m]\x1B[0m \x1B[1m", "")?.trim() ?? "", "formatEsbuildMessage");
try {
const esbuildError = error;
const formattedMessages = esbuildError.errors.map(formatEsbuildMessage);
message = `BuildError: ${formattedMessages.join("\n\n")}`;
processed = true;
} catch (_) {
}
}
if (!processed && error instanceof Error) {
if ("name" in error && typeof error.name === "string") code = error.name;
else if ("code" in error && typeof error.code === "string") code = error.code;
if ("message" in error && typeof error.message === "string") message = error.message;
else if ("reason" in error && typeof error.reason === "string") message = error.reason;
if ("stack" in error && typeof error.stack === "string") stack = error.stack;
stack = stack?.split("\n")?.filter((line) => !line.includes(error.message.trim()))?.join("\n") ?? "";
if (!code) code = "Unknown Error";
if (!message) message = "An unknown error occurred.";
if (!stack) stack = "";
}
if (error instanceof Error && error.cause && !isLifeError(error)) {
after += `${formatErrorForTerminal(error.cause)}`;
}
stack = stack.replace(/\/[^\s\n\r:;,()[\]{}'"<>]+/g, (match) => {
try {
if (path.isAbsolute(match)) {
const relativePath = path.relative(process.cwd(), match);
if (relativePath.length < match.length) return relativePath;
}
return match;
} catch {
return match;
}
});
return `${code}${code ? ": " : ""}${message}${message ? " " : ""}${stack ? `
${stack}` : ""}${after ? `
${after}` : ""}`;
}
__name(formatErrorForTerminal, "formatErrorForTerminal");
function formatLogForTerminal(log) {
let style;
if (log.level === "fatal")
style = { prefix: themeChalk.level.fatal.bold("\u2718"), color: themeChalk.level.fatal };
else if (log.level === "error")
style = { prefix: themeChalk.level.error.bold("\u2718"), color: themeChalk.level.error };
else if (log.level === "warn")
style = { prefix: themeChalk.level.warn.bold("\u25B2"), color: themeChalk.level.warn };
else if (log.level === "info")
style = { prefix: themeChalk.level.info.bold("\u29BF"), color: themeChalk.level.info };
else style = { prefix: themeChalk.level.debug.bold("\u2234"), color: themeChalk.level.debug };
const scopeDefinition = telemetryNodeScopesDefinition?.[log.scope] ?? telemetryBrowserScopesDefinition?.[log.scope];
const scopeDisplayName = scopeDefinition?.displayName instanceof Function ? (
// biome-ignore lint/suspicious/noExplicitAny: fine here
scopeDefinition.displayName(log.attributes)
) : scopeDefinition?.displayName;
const scope = `${chalk2.gray(`[${chalk2.italic(scopeDisplayName ?? "Unknown")}]`)} `;
const message = log.message || "";
const header = `${style.prefix} ${scope}${style.color ? style.color(message) : message}`;
const error = formatErrorForTerminal(log.error);
const errorColor = ["error", "fatal"].includes(log.level) ? themeChalk.level.error : themeChalk.level.warn;
let output = header;
if (error)
output += `
${errorColor.dim("-----")}
${errorColor(error)}
${errorColor.dim("-----")}`;
return output;
}
__name(formatLogForTerminal, "formatLogForTerminal");
// cli/commands/build/index.ts
import { resolve as resolve2 } from "path";
import { Command } from "commander";
// cli/utils/header.ts
import chalk4 from "chalk";
// cli/utils/version.ts
import chalk3 from "chalk";
import { getLatestVersion } from "fast-npm-meta";
async function getVersion() {
const currentVersion = package_default.version;
try {
const latestVersionData = await getLatestVersion("life");
const latestVersion = latestVersionData.version;
return {
current: currentVersion,
latest: latestVersion ?? void 0,
hasUpdate: currentVersion !== latestVersion
};
} catch {
return {
current: currentVersion,
hasUpdate: false
};
}
}
__name(getVersion, "getVersion");
function formatVersion(versionInfo) {
const hasUpdate = versionInfo.hasUpdate && versionInfo.latest;
const raw = hasUpdate ? `${versionInfo.current} (\u2191 ${versionInfo.latest})` : versionInfo.current;
const output = hasUpdate ? `${themeChalk.gray.medium(versionInfo.current)} ${chalk3.green(chalk3.bold(`(\u2191 ${versionInfo.latest})`))}` : themeChalk.gray.medium(versionInfo.current);
return { raw, output };
}
__name(formatVersion, "formatVersion");
// cli/utils/header.ts
async function generateHeader(name) {
const nameLength = `Life.js ${name}`.length;
const formattedVersion = formatVersion(await getVersion());
const gap = 13;
const padding = 1;
const headerSeparator = chalk4.gray(
"\u2500".repeat(nameLength + gap + formattedVersion.raw.length + padding * 2)
);
return `
${headerSeparator}
${" ".repeat(padding)}${themeChalk.gray.medium("Life.js")} ${themeChalk.orange(chalk4.italic(name))}${" ".repeat(gap)}${formattedVersion.output}${" ".repeat(padding)}
${headerSeparator}
`;
}
__name(generateHeader, "generateHeader");
// compiler/index.ts
import { createHash } from "crypto";
import { access, mkdir, readFile, rm, writeFile } from "fs/promises";
import os from "os";
import path3, { dirname, join, relative } from "path";
import { Lang, parseAsync as parseAsync2 } from "@ast-grep/napi";
import chalk5 from "chalk";
import chokidar from "chokidar";
import esbuild2 from "esbuild";
import { globbySync } from "globby";
import ts from "typescript";
// compiler/helpers/dependencies-map.ts
import fs from "fs/promises";
import path2 from "path";
import { parseAsync } from "oxc-parser";
import { walk } from "oxc-walker";
import resolve from "resolve";
var EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".json"];
var detectLanguage = /* @__PURE__ */ __name((f) => {
const ext = path2.extname(f).toLowerCase();
if (ext === ".tsx") return "tsx";
if (ext === ".ts" || ext === ".mts" || ext === ".cts") return "ts";
if (ext === ".jsx") return "jsx";
return "js";
}, "detectLanguage");
var resolveLocalFrom = /* @__PURE__ */ __name((spec, basedir) => {
try {
const resolved = resolve.sync(spec, {
basedir,
extensions: EXTENSIONS,
preserveSymlinks: true,
includeCoreModules: false
});
return resolved.includes("/node_modules/") ? null : resolved;
} catch {
return null;
}
}, "resolveLocalFrom");
var isTypeOnlyImport = /* @__PURE__ */ __name((node) => {
if (node.type === "ImportDeclaration" && node.importKind === "type") {
return true;
}
if (node.type === "ImportDeclaration" && node.specifiers) {
if (node.specifiers.length === 0) {
return false;
}
const hasValueImports = node.specifiers.some((spec) => {
if (spec.type === "ImportDefaultSpecifier" || spec.type === "ImportNamespaceSpecifier") {
return true;
}
return spec.type === "ImportSpecifier" && spec.importKind !== "type";
});
return !hasValueImports;
}
return false;
}, "isTypeOnlyImport");
var getDependenciesMap = /* @__PURE__ */ __name(async (entries, exclude = [], skipTypeOnlyDependencies = false) => {
const output = /* @__PURE__ */ new Set();
const entriesArray = Array.isArray(entries) ? entries : [entries];
for (const p of entriesArray) {
if (!path2.isAbsolute(p))
return failure({
code: "Validation",
message: `Provided entry path must be absolute: ${p}`
});
}
const entriesSet = new Set(entriesArray.map((p) => path2.resolve(p)));
const excludeArray = Array.isArray(exclude) ? exclude : [exclude];
for (const p of excludeArray) {
if (p && !path2.isAbsolute(p))
return failure({
code: "Validation",
message: `Provided exclude path must be absolute: ${p}`
});
}
const excludeSet = new Set(excludeArray.filter(Boolean).map((p) => path2.resolve(p)));
const queue = [...entriesSet];
const visited = /* @__PURE__ */ new Set();
while (queue.length) {
const file = queue.pop();
if (!file) throw new Error("Shouldn't happen");
if (visited.has(file) || excludeSet.has(file)) continue;
visited.add(file);
let content;
try {
content = await fs.readFile(file, "utf8");
} catch {
continue;
}
let ast;
try {
ast = await parseAsync(file, content, {
sourceType: "module",
lang: detectLanguage(file)
});
} catch {
continue;
}
const specifiers = /* @__PURE__ */ new Set();
walk(ast.program, {
enter(node) {
if (node.type === "ImportDeclaration" && node.source?.value) {
if (skipTypeOnlyDependencies && isTypeOnlyImport(node)) {
return;
}
specifiers.add(node.source.value);
}
if ((node.type === "ExportAllDeclaration" || node.type === "ExportNamedDeclaration") && node.source?.value) {
if (skipTypeOnlyDependencies && node.exportKind === "type") {
return;
}
specifiers.add(node.source.value);
}
if (node.type === "ImportExpression" && node.source?.type === "Literal" && typeof node.source.value === "string") {
specifiers.add(node.source.value);
}
if (node.type === "CallExpression" && node.arguments?.length === 1) {
const arg = node.arguments[0];
const literalArg = arg && arg.type === "Literal" && typeof arg.value === "string" ? arg.value : null;
if (literalArg && node.callee.type === "Identifier" && node.callee.name === "require") {
specifiers.add(literalArg);
} else if (literalArg && node.callee.type === "MemberExpression" && node.callee.object.type === "Identifier" && node.callee.object.name === "require" && node.callee.property.type === "Identifier" && node.callee.property.name === "resolve") {
specifiers.add(literalArg);
}
}
}
});
const basedir = path2.dirname(file);
for (const spec of specifiers) {
const resolved = resolveLocalFrom(spec, basedir);
if (!resolved || excludeSet.has(resolved)) continue;
if (!entriesSet.has(resolved)) output.add(resolved);
if (!visited.has(resolved)) queue.push(resolved);
}
}
return success(Array.from(output));
}, "getDependenciesMap");
// compiler/options.ts
import z2 from "zod";
var compilerOptionsSchema = z2.object({
projectDirectory: z2.string(),
outputDirectory: z2.string().prefault(".life"),
watch: z2.boolean().prefault(false),
stopOnError: z2.boolean().prefault(true)
});
// compiler/index.ts
var EXCLUDED_DEFAULTS = ["**/node_modules/**", "**/build/**", "**/generated/**", "**/dist/**"];
var LifeCompiler = class {
static {
__name(this, "LifeCompiler");
}
options;
hashes = /* @__PURE__ */ new Map();
watcher = null;
paths = {
configs: /* @__PURE__ */ new Set(),
servers: /* @__PURE__ */ new Set(),
clients: /* @__PURE__ */ new Set(),
dependencies: /* @__PURE__ */ new Map(),
// entryPath -> dependencies
serverBuilds: /* @__PURE__ */ new Map(),
// entryPath -> buildPath
clientBuilds: /* @__PURE__ */ new Map()
// entryPath -> buildPath
};
// Language Service for faster type extraction
telemetry;
#serverBundleContext = null;
#languageService;
#serviceHost;
#virtualFiles = /* @__PURE__ */ new Map();
#pluginNamesCache = /* @__PURE__ */ new Map();
constructor(options) {
this.options = compilerOptionsSchema.parse(options);
if (this.options.watch && options?.stopOnError === void 0) {
this.options.stopOnError = false;
}
this.telemetry = createTelemetryClient("compiler", {
watch: this.options.watch
});
this.options.outputDirectory = this.options.outputDirectory.startsWith("/") ? this.options.outputDirectory : join(this.options.projectDirectory, this.options.outputDirectory);
}
async start() {
return await this.telemetry.trace("start()", async (span) => {
try {
span.log.info({ message: "Starting compiler." });
span.log.debug({ message: `Project directory: ${this.options.projectDirectory}` });
span.log.debug({ message: `Output directory: ${this.options.outputDirectory}` });
this.telemetry.counter("compiler_started").increment();
await this.telemetry.trace("initial-compilation", async (spanCompilation) => {
await Promise.all([this.ensureBuildDirectory(), this.linkBuildDirectory()]);
const entryPaths = globbySync(
[
"**/agent/{server.ts,client.ts}",
"**/agents/*/{server.ts,client.ts}",
"**/life.config.ts"
],
{
cwd: this.options.projectDirectory,
ignore: EXCLUDED_DEFAULTS,
dot: false,
onlyFiles: true,
absolute: true,
gitignore: true,
unique: true
}
);
await Promise.all(entryPaths.map(async (p) => this.refreshEntryPathDependencies(p)));
const configResults = await Promise.all(
entryPaths.filter((p) => p.endsWith("life.config.ts")).map(
async (absPath) => await this.processFileEvent({
action: "added",
absPath,
type: "config",
noTimingLogs: true
})
)
);
const agentResults = await Promise.all([
...entryPaths.filter((p) => p.endsWith("server.ts")).map(
async (absPath) => await this.processFileEvent({
action: "added",
absPath,
type: "server",
noTimingLogs: true
})
),
...entryPaths.filter((p) => p.endsWith("client.ts")).map(
async (absPath) => await this.processFileEvent({
action: "added",
absPath,
type: "client",
noTimingLogs: true
})
)
]);
const results = [...configResults, ...agentResults];
spanCompilation.end();
const duration = spanCompilation.getData().duration;
const errorsCount = results.filter((r) => Boolean(r?.[0])).length;
const hasAgents = entryPaths.filter((p) => p.endsWith("server.ts")).length > 0;
if (hasAgents) {
span.log.info({
message: `Initial compilation in ${chalk5.bold(`${ns.toMs(duration)}ms`)}. ${errorsCount > 0 ? chalk5.red(`(${chalk5.bold(errorsCount)} error${errorsCount > 1 ? "s" : ""})`) : chalk5.dim("(no errors)")}`
});
} else
span.log.info({
message: "No agent server to compile yet. Create a first `agent/server.ts` file in your project."
});
});
if (this.options.watch) {
this.watchEntryPaths();
span.log.info({ message: "Watching for changes..." });
const handleShutdown = /* @__PURE__ */ __name(async (signal) => {
console.log("");
this.telemetry.log.info({ message: `Received ${signal}, shutting down gracefully...` });
await this.stop();
}, "handleShutdown");
process.once("SIGINT", () => handleShutdown("SIGINT"));
process.once("SIGTERM", () => handleShutdown("SIGTERM"));
} else await this.stop();
return success();
} catch (error) {
return failure({ code: "Unknown", cause: error });
}
});
}
#stopStarted = false;
async stop() {
return await this.telemetry.trace("stop()", async (span) => {
if (this.#stopStarted) return;
this.#stopStarted = true;
span.log.info({ message: "Stopping compiler." });
this.#serverBundleContext?.dispose();
this.#serverBundleContext = null;
await this.watcher?.close();
this.watcher = null;
await this.telemetry.flushConsumers();
});
}
async ensureBuildDirectory() {
await Promise.all([
mkdir(path3.join(this.options.outputDirectory, "server", "raw"), { recursive: true }),
mkdir(path3.join(this.options.outputDirectory, "server", "dist"), { recursive: true }),
mkdir(path3.join(this.options.outputDirectory, "server", "signal"), { recursive: true }),
mkdir(path3.join(this.options.outputDirectory, "client"), { recursive: true })
]);
}
async linkBuildDirectory() {
const generatedClientPath = join(this.options.outputDirectory, "client", "index.ts");
const generatedServerPath = join(this.options.outputDirectory, "server", "dist", "index.js");
const [errDistDir, distDir] = await this.findDistDirectory();
if (errDistDir) return failure(errDistDir);
const distFiles = globbySync("**/*", {
cwd: distDir,
onlyFiles: true,
absolute: false
});
const CODE_FILE_EXTENSIONS = [".js", ".mjs", ".cjs", ".ts", ".mts", ".cts", ".jsx", ".tsx"];
const codeFiles = distFiles.filter((file) => {
const ext = path3.extname(file).toLowerCase();
return CODE_FILE_EXTENSIONS.includes(ext);
});
await Promise.all(
codeFiles.map(async (file) => {
const filePath = join(distDir, file);
const content = await readFile(filePath, "utf-8");
const clientPath = relative(dirname(filePath), generatedClientPath);
const serverPath = relative(dirname(filePath), generatedServerPath);
const updatedContent = content.replaceAll(/"LIFE()_CLIENT_BUILD_PATH"/g, `"${clientPath}"`).replaceAll(/String\("LIFE()_CLIENT_BUILD_MODULE"\)/g, `import("${clientPath}")`).replaceAll(/"LIFE()_CLIENT_BUILD_MODULE"/g, `import("${clientPath}")`).replaceAll(/"LIFE()_SERVER_BUILD_PATH"/g, `"${serverPath}"`).replaceAll(/"LIFE()_BUILD_MODE"/g, '"production"').replaceAll(/'LIFE()_CLIENT_BUILD_PATH'/g, `'${clientPath}'`).replaceAll(/String\('LIFE()_CLIENT_BUILD_MODULE'\)/g, `import("${clientPath}")`).replaceAll(/'LIFE()_CLIENT_BUILD_MODULE'/g, `import("${clientPath}")`).replaceAll(/'LIFE()_SERVER_BUILD_PATH'/g, `'${serverPath}'`).replaceAll(/'LIFE()_BUILD_MODE'/g, "'production'");
if (updatedContent !== content) await writeFile(filePath, updatedContent, "utf-8");
})
);
}
async getPathDisplayName(_path, type) {
const relativePath = path3.relative(this.options.projectDirectory, _path);
if (["config", "dependency", "unknown"].includes(type)) return relativePath;
const clientContent = await readFile(_path, "utf-8");
const ast = await parseAsync2(Lang.TypeScript, clientContent);
const root = ast.root();
const defineCall = root.find({
rule: {
kind: "call_expression",
any: [
{ pattern: `defineAgent${type === "client" ? "Client" : ""}<$$$>($ARG)` },
{ pattern: `defineAgent${type === "client" ? "Client" : ""}($ARG)` }
]
}
});
if (!defineCall) return relativePath;
let name = defineCall?.getMatch("ARG")?.text() ?? null;
if (name) name = JSON.parse(name);
else return relativePath;
return name;
}
/**
* Main compiler entry point. Used to process a file.
* @param params - The parameters for the file event.
* @returns The result of the file event.
*/
async processFileEvent({
type,
action,
absPath,
noCache = false,
noTimingLogs = false
}) {
const result = await this.telemetry.trace("processFileEvent()", async (span) => {
span.setAttributes({ action, relPath: absPath });
const emitTimingLogs = /* @__PURE__ */ __name(async (recompiled) => {
if (noTimingLogs) return;
if (!["server", "client"].includes(type)) return;
span.end();
const name = await this.getPathDisplayName(absPath, type);
this.telemetry.log.info({
message: `Agent ${type} '${chalk5.bold.italic(name)}' ${recompiled ? "re-compiled" : "compiled"} in ${chalk5.bold(`${ns.toMs(span.getData().duration)}ms`)}.`,
attributes: { type, name }
});
}, "emitTimingLogs");
absPath = this.ensureAbsolute(absPath);
if (type === "unknown") return success();
if (action === "added") {
this.hashes.set(absPath, await this.hashFile(absPath));
span.log.debug({
message: `Added '${type}' path: ${absPath}`,
attributes: { path: absPath }
});
if (type === "config") return await this.onAddedConfig(absPath);
else if (type === "server") {
const res = await this.onAddedServer(absPath);
if (!res?.[0]) await emitTimingLogs(false);
return res;
} else if (type === "client") {
const res = await this.onAddedClient(absPath);
if (!res?.[0]) await emitTimingLogs(false);
return res;
} else if (type === "dependency")
return failure({
code: "Conflict",
message: "'added' action is not supported for dependency paths. Shouldn't happen."
});
} else if (action === "removed") {
this.hashes.delete(absPath);
span.log.debug({
message: `Removed '${type}' path: ${absPath}`,
attributes: { path: absPath }
});
if (type === "config") return await this.onRemovedConfig(absPath);
else if (type === "server") return await this.onRemovedServer(absPath);
else if (type === "client") return await this.onRemovedClient(absPath);
else if (type === "dependency") return await this.onRemovedDependency(absPath);
} else if (action === "changed") {
const newHash = await this.hashFile(absPath);
if (newHash === this.hashes.get(absPath) && !noCache) return success();
this.hashes.set(absPath, newHash);
span.log.debug({
message: `Changed '${type}' path: ${absPath}`,
attributes: { path: absPath }
});
if (type !== "dependency") await this.refreshEntryPathDependencies(absPath);
if (type === "config") return await this.onChangedConfig(absPath);
else if (type === "server") {
const res = await this.onChangedServer(absPath);
if (!res?.[0]) await emitTimingLogs(true);
return res;
} else if (type === "client") {
const res = await this.onChangedClient(absPath);
if (!res?.[0]) await emitTimingLogs(true);
return res;
} else if (type === "dependency") return await this.onChangedDependency(absPath);
}
throw new Error("Invalid action. Shouldn't happen.");
});
const [error] = result;
if (error) {
const displayName = await this.getPathDisplayName(absPath, type);
let baseMessage = "Failed to compile ";
if (["client", "server"].includes(type))
baseMessage += `agent ${type} '${chalk5.bold.italic(displayName)}'.`;
else baseMessage += `'${chalk5.bold.italic(displayName)}' ${type} file.`;
const relativePath = path3.relative(this.options.projectDirectory, absPath);
if (this.options.stopOnError) {
this.telemetry.log.error({ message: `${baseMessage}
Path: ${relativePath}`, error });
await this.stop();
} else
this.telemetry.log.warn({
message: `${baseMessage} It has been ignored.
Path: ${relativePath}`,
error
});
}
return result;
}
// Configs Events Handlers
async onAddedConfig(configPath) {
return await this.telemetry.trace(
"onAddedConfig()",
async () => {
try {
this.paths.configs.add(configPath);
return await this.onChangedConfig(configPath);
} catch (error) {
return failure({ code: "Unknown", cause: error });
}
},
{ attributes: { configPath } }
);
}
async onRemovedConfig(configPath) {
return await this.telemetry.trace(
"onRemovedConfig()",
async () => {
try {
this.paths.configs.delete(configPath);
return await this.onChangedConfig(configPath);
} catch (error) {
return failure({ code: "Unknown", cause: error });
}
},
{ attributes: { configPath } }
);
}
async onChangedConfig(configPath) {
return await this.telemetry.trace(
"onChangedConfig()",
async () => {
try {
const configContent = await readFile(configPath, "utf-8");
const ast = await parseAsync2(Lang.TypeScript, configContent);
const root = ast.root();
const defineConfigCall = root.find({
rule: { kind: "call_expression", pattern: "defineConfig($ARG)" }
});
if (!defineConfigCall) {
return failure({
code: "Validation",
message: "Config file does not contain a defineConfig() call.",
attributes: { path: configPath }
});
}
const exportDefaultStatement = root.find({
rule: {
kind: "export_statement",
pattern: "export default",
has: {
regex: "defineConfig(.*)"
}
}
});
if (!exportDefaultStatement) {
return failure({
code: "Validation",
message: `Config file has defineConfig() but doesn't export it as default. Use \`export default defineConfig(...)\` instead.`,
attributes: { path: configPath }
});
}
const affected = Array.from(this.paths.servers).filter(
(p) => this.isEntryPathTouchedByConfig(p, configPath)
);
await Promise.all(
affected.map(async (serverPath) => {
const absPath = this.ensureAbsolute(serverPath);
return await this.processFileEvent({
action: "changed",
absPath,
type: "server",
noCache: true
});
})
);
return success();
} catch (error) {
return failure({ code: "Unknown", cause: error });
}
},
{ attributes: { configPath } }
);
}
// Agent Servers Events Handlers
async onAddedServer(serverPath) {
return await this.telemetry.trace(
"onAddedServer()",
async () => {
try {
this.paths.servers.add(serverPath);
return await this.onChangedServer(serverPath);
} catch (error) {
return failure({ code: "Unknown", cause: error });
}
},
{ attributes: { serverPath } }
);
}
async onRemovedServer(serverPath) {
return await this.telemetry.trace(
"onRemovedServer()",
async () => {
try {
this.paths.servers.delete(serverPath);
const buildPath = this.paths.serverBuilds.get(serverPath);
if (buildPath) {
this.paths.serverBuilds.delete(serverPath);
await rm(buildPath);
}
const res = await this.generateServerBundle();
if (!res?.[0]) return res;
const name = this.getPathDisplayName(serverPath, "server");
const signalPath = path3.join(
this.options.outputDirectory,
"server",
"signal",
`${name}.txt`
);
if (await this.fileExists(signalPath)) {
await writeFile(signalPath, "removed", "utf-8");
}
return success();
} catch (error) {
return failure({ code: "Unknown", cause: error });
}
},
{ attributes: { serverPath } }
);
}
async onChangedServer(serverPath) {
return await this.telemetry.trace(
"onChangedServer()",
async (span) => {
try {
const serverContent = await readFile(serverPath, "utf-8");
const ast = await parseAsync2(Lang.TypeScript, serverContent);
const root = ast.root();
const defineAgentCall = root.find({
rule: { kind: "call_expression", pattern: "defineAgent($ARG)" }
});
if (!defineAgentCall) {
return failure({
code: "Validation",
message: `Agent server '${chalk5.bold.italic(path3.relative(this.options.projectDirectory, serverPath))}' doesn't contain a ${chalk5.bold.italic("defineAgent(...)")} call.`,
attributes: { serverPath }
});
}
const exportDefaultStatement = root.find({
rule: {
kind: "export_statement",
pattern: "export default",
has: {
regex: "defineAgent(.*)"
}
}
});
if (!exportDefaultStatement) {
return failure({
code: "Validation",
message: `Agent server '${chalk5.bold.italic(path3.relative(this.options.projectDirectory, serverPath))}' doesn't export ${chalk5.bold.italic("defineAgent(...)")} call as default. Use ${chalk5.bold.italic("export default defineAgent(...)")}.`,
attributes: { serverPath }
});
}
let name = defineAgentCall?.getMatch("ARG")?.text() ?? null;
if (name) name = JSON.parse(name);
if (!name) {
return failure({
code: "Validation",
message: `Agent server '${chalk5.bold.italic(name)}' has ${chalk5.bold.italic("defineAgent()")} but doesn't provide a name. Use ${chalk5.bold.italic("defineAgent(<name>)")}.`,
attributes: { serverPath }
});
}
const configPaths = [];
for (const configPath of this.paths.configs) {
if (this.isEntryPathTouchedByConfig(serverPath, configPath))
configPaths.push(configPath);
}
configPaths.sort((a, b) => b.length - a.length);
const treeFiles = /* @__PURE__ */ new Set([serverPath, ...configPaths]);
for (const file of deepClone(treeFiles)) {
const deps = this.paths.dependencies.get(file);
if (deps) for (const dep of deps) treeFiles.add(dep);
}
const treeHashes = Array.from(treeFiles).map((file) => this.hashes.get(file));
const filteredTreeHashes = treeHashes.filter((hash) => hash !== void 0);
if (treeHashes.length !== filteredTreeHashes.length) {
span.log.warn({
message: "Some tree files have no hash. Shouldn't happen.",
attributes: { treeFiles }
});
}
const sha = createHash("md5").update(treeHashes.join(":")).digest("hex");
const buildPath = path3.join(this.options.outputDirectory, "server", "raw", `${name}.ts`);
const relServerPath = path3.relative(path3.dirname(buildPath), serverPath);
const relConfigPaths = configPaths.map(
(configPath) => path3.relative(path3.dirname(buildPath), configPath)
);
const content = `
${relConfigPaths.map((configPath, i) => `import config${i} from "${configPath}";`).join("\n")}
import agent from "${relServerPath}";
export default {
definition: agent.def,
globalConfigs: [${configPaths.map((_, i) => `config${i}`).join(", ")}],
sha: "${sha}"
} as const;
`.trim();
await writeFile(buildPath, content, "utf-8");
this.paths.serverBuilds.set(serverPath, buildPath);
const [errBundle] = await this.generateServerBundle();
if (errBundle) return failure(errBundle);
const signalPath = path3.join(
this.options.outputDirectory,
"server",
"signal",
`${name}.txt`
);
await writeFile(signalPath, sha, "utf-8");
return success();
} catch (error) {
return failure({ code: "Unknown", cause: error });
}
},
{ attributes: { serverPath } }
);
}
// Agent Clients Events Handlers
async onAddedClient(clientPath) {
return await this.telemetry.trace(
"onAddedClient()",
async () => {
try {
this.paths.clients.add(clientPath);
return await this.onChangedClient(clientPath);
} catch (error) {
return failure({ code: "Unknown", cause: error });
}
},
{ attributes: { clientPath } }
);
}
async onRemovedClient(clientPath) {
return await this.telemetry.trace(
"onRemovedClient()",
async () => {
try {
this.paths.clients.delete(clientPath);
const buildPath = this.paths.clientBuilds.get(clientPath);
if (buildPath) {
this.paths.clientBuilds.delete(clientPath);
await rm(buildPath);
}
return await this.onChangedClient(clientPath);
} catch (error) {
return failure({ code: "Unknown", cause: error });
}
},
{ attributes: { clientPath } }
);
}
async onChangedClient(clientPath) {
return await this.telemetry.trace(
"onChangedClient()",
async () => {
try {
const clientContent = await readFile(clientPath, "utf-8");
const ast = await parseAsync2(Lang.TypeScript, clientContent);
const root = ast.root();
const defineAgentClientCall = root.find({
rule: {
kind: "call_expression",
any: [
{ pattern: "defineAgentClient<$$$>($ARG)" },
{ pattern: "defineAgentClient($ARG)" }
]
}
});
if (!defineAgentClientCall) {
return failure({
code: "Validation",
message: `Agent client '${chalk5.bold.italic(path3.relative(this.options.projectDirectory, clientPath))}' doesn't contain a ${chalk5.bold.italic("defineAgentClient(...)")} call. It has been ignored.`,
attributes: { clientPath }
});
}
const exportDefaultStatement = root.find({
rule: {
kind: "export_statement",
pattern: "export default",
has: {
regex: "defineAgentClient(<.*>)?(.*)"
}
}
});
if (!exportDefaultStatement) {
return failure({
code: "Validation",
message: `Agent client '${chalk5.bold.italic(path3.relative(this.options.projectDirectory, clientPath))}' doesn't export ${chalk5.bold.italic("defineAgentClient(...)")} call as default. It has been ignored. Use ${chalk5.bold.italic("export default defineAgentClient(...)")}.`,
attributes: { clientPath }
});
}
let name = defineAgentClientCall?.getMatch("ARG")?.text() ?? null;
if (name) name = JSON.parse(name);
if (!name) {
return failure({
code: "Validation",
message: `Agent client '${chalk5.bold.italic(path3.relative(this.options.projectDirectory, clientPath))}' has ${chalk5.bold.italic("defineAgentClient()")} but doesn't provide a name. It has been ignored. Use ${chalk5.bold.italic("defineAgentClient(<name>)")}.`,
attributes: { clientPath }
});
}
const pluginNames = await this.extractClientPluginNames(clientPath);
const plugins = pluginNames.map(
(pluginName) => ` "${pluginName}": {
def: p["${pluginName}"],
$types: {
atoms: (mock as typeof p["${pluginName}"]["atoms"])<PC["${pluginName}"]>(null as any),
class: (mock as typeof p["${pluginName}"]["class"])<PC["${pluginName}"]>(null as any),
clientConfig: {} as PC["${pluginName}"]["client"],
serverConfig: {} as PC["${pluginName}"]["server"],
}
}`
).join(",\n");
const buildPath = path3.join(this.options.outputDirectory, "client", `${name}.ts`);
const relClientPath = path3.relative(path3.dirname(buildPath), clientPath);
const content = `import agentClient from "${relClientPath.replace(".ts", "")}";
type SC = typeof agentClient["def"]["$serverDef"]["pluginConfigs"];
type CC = typeof agentClient["def"]["pluginConfigs"];
type PC = {
${pluginNames.map((pluginName) => ` "${pluginName}": { client: CC["${pluginName}"], server: SC extends { "${pluginName}": unknown } ? SC["${pluginName}"] : never }`).join(",\n")}
}
const p = {
${pluginNames.map((pluginName) => ` "${pluginName}": agentClient.def.plugins.find(p => p.name === "${pluginName}")!`).join(",\n")}
} as const;
const mock = (() => void 0) as any;
export default {
definition: agentClient.def,
plugins: {
${plugins}
}
} as const;
`.trim();
await writeFile(buildPath, content, "utf-8");
this.paths.clientBuilds.set(clientPath, buildPath);
const [errBundle] = await this.generateClientBundle();
if (errBundle) return failure(errBundle);
return success();
} catch (error) {
return failure({ code: "Unknown", cause: error });
}
},
{ attributes: { clientPath } }
);
}
// Dependency Paths Events Handlers
async onRemovedDependency(dependencyPath) {
return await this.telemetry.trace(
"onRemovedDependency()",
async () => {
try {
return await this.onChangedDependency(dependencyPath);
} catch (error) {
return failure({ code: "Unknown", cause: error });
}
},
{ attributes: { dependencyPath } }
);
}
async onChangedDependency(dependencyPath) {
return await this.telemetry.trace(
"onChangedDependency()",
async () => {
try {
const affected = /* @__PURE__ */ new Set();
for (const [entryPath, dependencies] of this.paths.dependencies) {
if (dependencies.has(dependencyPath)) affected.add(entryPath);
}
await Promise.all(
Array.from(affected).map((p) => {
const absPath = this.ensureAbsolute(p);
const type = this.getPathType(absPath);
return this.processFileEvent({ action: "changed", absPath, type, noCache: true });
})
);
return success();
} catch (error) {
return failure({ code: "Unknown", cause: error });
}
},
{ attributes: { dependencyPath } }
);
}
watchEntryPaths() {
return this.telemetry.trace("watchEntryPaths()", () => {
function isMacOS26_1_x() {
if (process.platform !== "darwin") return false;
const release = os.release();
const majorVersion = Number.parseInt(release?.split(".")[0] ?? "0", 10) ?? 0;
const minorVersion = Number.parseInt(release?.split(".")[1] ?? "0", 10) ?? 0;
return majorVersion >= 25 && minorVersion >= 1;
}
__name(isMacOS26_1_x, "isMacOS26_1_x");
const enableFSEventsWorkaround = isMacOS26_1_x();
this.watcher = chokidar.watch(".", {
cwd: this.options.projectDirectory,
ignoreInitial: true,
ignored: EXCLUDED_DEFAULTS,
awaitWriteFinish: {
stabilityThreshold: 20,
pollInterval: 5
},
// Apply workaround on affected macOS versions
...enableFSEventsWorkaround && {
usePolling: true,
interval: 100
}
});
const onWatcherEventFn = /* @__PURE__ */ __name((action) => (p) => {
const absPath = this.ensureAbsolute(p);
const type = this.getPathType(absPath);
this.processFileEvent({ action, absPath, type });
}, "onWatcherEventFn");
this.watcher.on("add", onWatcherEventFn("added"));
this.watcher.on("unlink", onWatcherEventFn("removed"));
this.watcher.on("change", onWatcherEventFn("changed"));
});
}
async refreshEntryPathDependencies(entryPath) {
const absPath = this.ensureAbsolute(entryPath);
const exclude = [];
if (this.getPathType(absPath) === "client") {
exclude.push(absPath.replace("/client.ts", "/server.ts"));
}
const [error, dependencies] = await getDependenciesMap(absPath, exclude, true);
if (error) {
this.telemetry.log.error({
message: "Obtaining entry path dependencies failed.",
error,
attributes: { entryPath }
});
return;
}
this.paths.dependencies.set(entryPath, new Set(dependencies));
await Promise.all(dependencies.map(async (d) => this.hashes.set(d, await this.hashFile(d))));
}
getPathType(absPath) {
const pathParts = absPath.split("/");
const parentDir = pathParts.at(-2);
const grandParentDir = pathParts.at(-3);
if (absPath.endsWith("/life.config.ts")) return "config";
else if (
// Match only agent/* or agents/<name>/*
(parentDir === "agent" || grandParentDir === "agents") && // Exclude false positives if a plugins folder is present at the root of agents/
!absPath.includes("/plugins/")
) {
if (absPath.endsWith("/server.ts")) return "server";
else if (absPath.endsWith("/client.ts")) return "client";
}
for (const dependencies of this.paths.dependencies.values()) {
if (dependencies.has(absPath)) return "dependency";
}
return "unknown";
}
ensureAbsolute(p) {
return path3.resolve(this.options.projectDirectory, p);
}
async hashFile(filePath) {
const content = await readFile(filePath, "utf-8");
return createHash("md5").update(content).digest("hex");
}
isEntryPathTouchedByConfig(entryPath, configPath) {
const configDir = path3.dirname(configPath);
const relativePath = path3.relative(configDir, entryPath);
return !relativePath.startsWith("..");
}
/**
* Finds the absolute path to node_modules/life/dist/
* @param startPath - The path to start searching from
* @returns The absolute path to node_modules/life/dist/
*/
async findDistDirectory() {
const currentFilePath = import.meta.url.replace("file://", "");
let packageRoot = null;
let searchDir = dirname(currentFilePath);
for (let i = 0; i < 10; i++) {
const packageJsonPath = join(searchDir, "package.json");
if (await this.fileExists(packageJsonPath)) {
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8"));
if (packageJson.name === "life") {
packageRoot = searchDir;
break;
}
}
searchDir = dirname(searchDir);
}
if (!packageRoot)
return failure({
code: "NotFound",
message: "Could not find life package root."
});
const distDir = join(packageRoot, "dist");
if (!await this.fileExists(distDir))
return failure({
code: "NotFound",
message: "Could not find life dist directory."
});
return success(distDir);
}
// -------------------------------------
async generateServerBundle() {
return await this.telemetry.trace("generateServerBundle()", async () => {
try {
const serversMap = {};
for (const serverPath of this.paths.serverBuilds.values()) {
const name = path3.basename(serverPath, ".ts");
serversMap[name] = serverPath;
}
const indexPath = path3.join(this.options.outputDirectory, "server", "raw", "index.ts");
const indexContent = `${Object.entries(serversMap).map(
([name, p]) => `import ${name} from "./${path3.relative(path3.dirname(indexPath), p).replace(".ts", "")}";`
).join("\n")}
export default {
${Object.keys(serversMap).map((name) => `"${name}": ${name}`).join(",\n")}
}
`.trim();
await writeFile(indexPath, indexContent, "utf-8");
if (this.#serverBundleContext) await this.#serverBundleContext.cancel();
else {
this.#serverBundleContext = await esbuild2.context({
entryPoints: [indexPath],
outdir: path3.join(this.options.outputDirectory, "server", "dist"),
bundle: true,
format: "esm",
platform: "node",
target: "node20",
packages: "external",
keepNames: true,
jsx: "automatic",
write: true,
logLevel: "silent",
treeShaking: true,
loader: {
".node": "file"
},
minify: true
});
}
await this.#serverBundleContext.rebuild();
return success();
} catch (error) {
return failure({ code: "Unknown", cause: error });
}
});
}
async generateClientBundle() {
return await this.telemetry.trace("generateClientBundle()", async () => {
try {
const clientsMap = {};
for (const clientPath of this.paths.clientBuilds.values()) {
const name = path3.basename(clientPath, ".ts");
clientsMap[name] = clientPath;
}
const indexPath = path3.join(this.options.outputDirectory, "client", "index.ts");
const indexContent = `${Object.entries(clientsMap).map(
([name, p]) => `import ${name} from "./${path3.relative(path3.dirname(indexPath), p).replace(".ts", "")}";`
).join("\n")}
export default {
${Object.keys(clientsMap).map((name) => `"${name}": ${name}`).join(",\n")}
}
`.trim();
await writeFile(indexPath, indexContent, "utf-8");
return success();
} catch (error) {
return failure({ code: "Unknown", cause: error });
}
});
}
async fileExists(filePath) {
try {
await access(filePath);
return true;
} catch {
return false;
}
}
fileVersions = /* @__PURE__ */ new Map();
initLanguageService() {
if (this.#languageService) return;
const configPath = ts.findConfigFile(
this.options.projectDirectory,
ts.sys.fileExists,
"tsconfig.json"
);
if (!configPath) return;
const { config } = ts.readConfigFile(configPath, ts.sys.readFile);
const parsedConfig = ts.parseJsonConfigFileContent(
config,
ts.sys,
this.options.projectDirectory
);
parsedConfig.options.skipLibCheck = true;
parsedConfig.options.skipDefaultLibCheck = true;
this.#serviceHost = {
getScriptFileNames: /* @__PURE__ */ __name(() => Array.from(this.#virtualFiles.keys()), "getScriptFileNames"),
getScriptVersion: /* @__PURE__ */ __name((fileName) => {
return String(this.fileVersions.get(fileName) || 1);
}, "getScriptVersion"),
getScriptSnapshot: /* @__PURE__ */ __name((fileName) => {
const virtualContent = this.#virtualFiles.get(fileName);
if (virtualContent) {
return ts.ScriptSnapshot.fromString(virtualContent);
}
const content = ts.sys.readFile(fileName);
return content ? ts.ScriptSnapshot.fromString(content) : void 0;
}, "getScriptSnapshot"),
getCurrentDirectory: /* @__PURE__ */ __name(() => this.options.projectDirectory, "getCurrentDirectory"),
getCompilationSettings: /* @__PURE__ */ __name(() => parse