Files
windmill/cli/windmill-utils-internal/src/inline-scripts/replacer.ts
centdix d4a1b4abed add aiagent module support to inline script extraction/replacement (#7773)
* dual build for utils-internal

* bump version

* feat(cli): add aiagent module support to inline script extraction/replacement

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* add missing field in openapi

* bump yaml validator version

* cleaning

* cleaning

* cleaning

* nit

* cleaning

* cleaning

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 18:00:34 +00:00

121 lines
4.1 KiB
TypeScript

import { FlowModule, RawScript } from "../gen/types.gen.ts";
async function replaceRawscriptInline(
id: string,
rawscript: RawScript,
fileReader: (path: string) => Promise<string>,
logger: { info: (message: string) => void; error: (message: string) => void },
separator: string,
removeLocks?: string[]
): Promise<void> {
if (!rawscript.content || !rawscript.content.startsWith("!inline")) {
return;
}
const path = rawscript.content.split(" ")[1];
const pathSuffix = path.split(".").slice(1).join(".");
const newPath = id + "." + pathSuffix;
try {
rawscript.content = await fileReader(path);
} catch {
logger.error(`Script file ${path} not found`);
try {
rawscript.content = await fileReader(newPath);
} catch {
logger.error(`Script file ${newPath} not found`);
}
}
const lock = rawscript.lock;
if (removeLocks && removeLocks.includes(path)) {
rawscript.lock = undefined;
} else if (
lock &&
typeof lock === "string" &&
lock.trimStart().startsWith("!inline ")
) {
const lockPath = lock.split(" ")[1];
try {
rawscript.lock = await fileReader(lockPath.replaceAll("/", separator));
} catch {
logger.error(`Lock file ${lockPath} not found, treating as empty`);
rawscript.lock = "";
}
}
}
/**
* Replaces inline script references with actual file content from the filesystem.
* This function recursively processes all flow modules and their nested structures.
*
* @param modules - Array of flow modules to process
* @param fileReader - Function to read file content (typically fs.readFile or similar)
* @param logger - Optional logger object with info and error methods
* @param localPath - Base path for resolving relative file paths
* @param removeLocks - Optional array of paths for which to remove lock files
* @returns Promise that resolves when all inline scripts have been replaced
*/
export async function replaceInlineScripts(
modules: FlowModule[],
fileReader: (path: string) => Promise<string>,
logger: {
info: (message: string) => void,
error: (message: string) => void,
} = {
info: () => {},
error: () => {},
},
localPath: string,
separator: string = "/",
removeLocks?: string[],
// renamer?: (path: string, newPath: string) => void,
// deleter?: (path: string) => void
): Promise<void> {
await Promise.all(modules.map(async (module) => {
if (!module.value) {
throw new Error(`Module value is undefined for module ${module.id}`);
}
if (module.value.type === "rawscript") {
await replaceRawscriptInline(
module.id,
module.value,
fileReader,
logger,
separator,
removeLocks
);
} else if (module.value.type === "forloopflow" || module.value.type === "whileloopflow") {
await replaceInlineScripts(module.value.modules, fileReader, logger, localPath, separator, removeLocks);
} else if (module.value.type === "branchall") {
await Promise.all(module.value.branches.map(async (branch) => {
await replaceInlineScripts(branch.modules, fileReader, logger, localPath, separator, removeLocks);
}));
} else if (module.value.type === "branchone") {
await Promise.all(module.value.branches.map(async (branch) => {
await replaceInlineScripts(branch.modules, fileReader, logger, localPath, separator, removeLocks);
}));
await replaceInlineScripts(module.value.default, fileReader, logger, localPath, separator, removeLocks);
} else if (module.value.type === "aiagent") {
await Promise.all((module.value.tools ?? []).map(async (tool) => {
const toolValue = tool.value;
if (
!toolValue ||
toolValue.tool_type !== "flowmodule" ||
toolValue.type !== "rawscript"
) {
return;
}
await replaceRawscriptInline(
tool.id,
toolValue,
fileReader,
logger,
separator,
removeLocks
);
}));
}
}));
}