Fl
libraryReads, writes, copies, moves, removes, inspects and searches files in the project workspace. Paths are relative to the workspace of the run, which is the project folder on the agent. Text files are read and written in the JVM default charset (UTF-8 on current Java versions). When a script runs through a Reflex server, a file or folder that is not on the agent yet is first fetched from the project's files on the server, `Fl.remove` also deletes it there, and the files a run leaves behind (except `.lua` files) are copied back to the project when the run ends. A script run directly on the agent (for example with the CLI) only uses its local project folder. By default paths are not confined to the workspace: `..` is followed, and so are absolute paths when the script runs directly on the agent. An agent started with `agent.files.confineToWorkspace=true` (`AGENT_FILES_CONFINE_TO_WORKSPACE`) rejects absolute paths and anything that leaves the workspace, symlinks included, in every file function of the libraries.
- Kind Library, a global in every script
- Category Files & Documents
- Functions 13
Functions
-
mkdirself Creates a directory including any missing parent directories. -
getFilestable Lists the files directly inside a directory, most recently modified first. -
beingWrittenboolean Tells whether a file is still growing: reads its size, waits 500 ms, reads it again and returns `true` when the size changed. -
removeself Deletes a file, or a directory together with everything in it. -
copyself Copies one file. -
existsboolean Checks whether a file or directory exists. -
moveself Moves a file or directory to a new path. -
renameself Renames a file or directory inside its current directory. -
statFileStat Returns metadata of a file or directory: type, size in bytes and timestamps. -
findtable Searches a directory recursively and returns every file and directory found below it (not the directory itself), sorted alphabetically. -
globtable Returns the files and directories whose path matches a glob pattern, searching the whole workspace or the directory given as first argument. -
readtable Reads a text file and returns its lines as a list. -
writeself Writes text to a file and creates missing parent directories.
mkdir()
Section titled “mkdir()”Fl.mkdir(folderDir: string)→self Creates a directory including any missing parent directories. Does nothing when it exists.
Parameters
-
folderDirstring - Directory to create, relative to the workspace
Returns
getFiles()
Section titled “getFiles()”Fl.getFiles(location: string)→table Lists the files directly inside a directory, most recently modified first. Subdirectories are not listed and the search is not recursive: use `Fl.find` or `Fl.glob` for that. Every entry is the path of the file relative to the workspace, so it starts with the directory you passed (`./report.csv` for `.`, `exports/report.csv` for `exports`). A directory that does not exist gives an empty list.
Parameters
-
locationstring - Directory to list, relative to the workspace
Returns
Example runs in CI
Fl.write("exports/a.txt", "a")Fl.write("exports/b.txt", "b")for _, file in ipairs(Fl.getFiles("exports")) do print(file)endbeingWritten()
Section titled “beingWritten()”Fl.beingWritten(file: string)→boolean Tells whether a file is still growing: reads its size, waits 500 ms, reads it again and returns `true` when the size changed. Use it to wait until a download or export has finished before reading the file. Blocks the script for 500 ms and throws when the file does not exist.
Parameters
-
filestring - File to check, relative to the workspace
Returns
remove()
Section titled “remove()”Fl.remove(fileLua: string)→self Deletes a file, or a directory together with everything in it. When the script runs through a Reflex server the file or directory is also deleted from the project's files on the server. Throws an error when the path does not exist.
Parameters
-
fileLuastring - File or directory to delete, relative to the workspace
Returns
Example runs in CI
Fl.write("scratch/tmp.txt", "temporary")Fl.remove("scratch")print(Fl.exists("scratch"))copy()
Section titled “copy()”Fl.copy(source: string, target: string)→self Copies one file. The target is overwritten when it exists and its missing parent directories are created. Directories cannot be copied: the call throws `Failed to copy file`, as it does when the source is missing.
Parameters
-
sourcestring - File to copy, relative to the workspace
-
targetstring - Path of the copy, relative to the workspace
Returns
exists()
Section titled “exists()”Fl.exists(location: string)→boolean Checks whether a file or directory exists. When the script runs through a Reflex server, a path that is not on the agent yet is looked up in the project's files on the server and fetched, so project files count as existing.
Parameters
-
locationstring - File or directory, relative to the workspace
Returns
move()
Section titled “move()”Fl.move(source: string, target: string)→self Moves a file or directory to a new path. Missing parent directories of the target are created and an existing target file is replaced. Throws `Source path not found` when the source does not exist. To change only the name, use `Fl.rename`.
Parameters
-
sourcestring - File or directory to move, relative to the workspace
-
targetstring - New path, relative to the workspace
Returns
rename()
Section titled “rename()”Fl.rename(source: string, newName: string)→self Renames a file or directory inside its current directory. An existing item with the new name is replaced. Throws when the new name contains a directory part or is absolute, and when the source does not exist. To move something to another directory, use `Fl.move`.
Parameters
-
sourcestring - File or directory to rename, relative to the workspace
-
newNamestring - New name only, without any directory, like `new.txt`
Returns
stat()
Section titled “stat()”Fl.stat(location: string)→FileStat Returns metadata of a file or directory: type, size in bytes and timestamps. A path that does not exist is not an error: the table then only has `path` and `exists = false`.
Parameters
-
locationstring - File or directory, relative to the workspace
Returns
Fields of FileStat
-
pathstring - the path relative to the workspace, with forward slashes (the workspace itself is .)
-
existsboolean - whether the path exists
-
namestring optional - the last part of the path
-
typestring optional - file, directory or other (for example a device)
-
isFileboolean optional - true for a regular file
-
isDirectoryboolean optional - true for a directory
-
sizenumber optional - size in bytes (for a directory this is whatever the file system reports, not the size of its content)
-
modifiedAtstring optional - last modification time as an ISO 8601 instant in UTC, for example 2026-01-31T12:30:00.123Z
-
createdAtstring optional - creation time as an ISO 8601 instant in UTC; some file systems report the modification time
-
accessedAtstring optional - last access time as an ISO 8601 instant in UTC
-
parentstring optional - the path of the containing directory relative to the workspace; left out when it lies outside the workspace
Example runs in CI
Fl.write("report.txt", "hello")local info = Fl.stat("report.txt")print(info.exists, info.type, info.size, info.modifiedAt)print(Fl.stat("missing.txt").exists)find()
Section titled “find()”Fl.find(root: string)→tableFl.find(root: string, pattern: string)→table Searches a directory recursively and returns every file and directory found below it (not the directory itself), sorted alphabetically. With a pattern only the matching entries are returned. When `root` is a single file, the result is that file (if it matches). A path that does not exist gives an empty list. The returned paths are relative to the workspace (not to `root`) and always use `/`. The pattern is a glob such as `*.csv` or `reports/**/*.pdf`, matched against the path relative to `root`. A pattern without `/` is also matched against the bare name of every entry, so `*.csv` finds CSV files at any depth, and a leading `**/` also matches entries directly in `root`. `Fl.glob` does the same with the pattern as the main argument.
Parameters
-
rootstring - Directory or file to search from, relative to the workspace
-
patternstring optional - Glob pattern the entries must match, like `*.csv`
Returns
Example runs in CI
Fl.write("data/a.csv", "1")Fl.write("data/nested/b.csv", "2")Fl.write("data/c.txt", "3")for _, path in ipairs(Fl.find("data", "*.csv")) do print(path)endglob()
Section titled “glob()”Fl.glob(pattern: string)→tableFl.glob(root: string, pattern: string)→table Returns the files and directories whose path matches a glob pattern, searching the whole workspace or the directory given as first argument. Same as `Fl.find` with a pattern, including how patterns are matched: `*.csv` matches CSV files at any depth. Paths are relative to the workspace, use `/` and are sorted alphabetically.
Parameters
-
rootstring - Directory to search from, relative to the workspace
-
patternstring - Glob pattern like `*.csv` or `reports/**/*.pdf`
Returns
Example runs in CI
Fl.write("out/one.csv", "1")Fl.write("out/two.csv", "2")print(#Fl.glob("*.csv"))print(#Fl.glob("out", "one.*"))read()
Section titled “read()”Fl.read(fileLoc: string)→table Reads a text file and returns its lines as a list. Line breaks are removed, a final line break does not add an empty entry and an empty file gives an empty list. The whole file is read into memory. Throws `File not found` when the file cannot be read. For structured files use `Json`, `Yaml`, `Csv`, `Xml` or `Xlsx`.
Parameters
-
fileLocstring - File to read, relative to the workspace
Returns
Example runs in CI
Fl.write("notes.txt", "first line\nsecond line")local lines = Fl.read("notes.txt")print(#lines, lines[1])print(Fl.exists("notes.txt"))write()
Section titled “write()”Fl.write(fileName: string, content: string)→selfFl.write(fileName: string, content: string, append: boolean, newLine: boolean)→self Writes text to a file and creates missing parent directories. With two arguments the text is appended to the file, not written over it: when the file already has content a line break is inserted first, so every call adds one line and the file never ends with a trailing line break. To replace the content of the file call it with four arguments and `append = false`. Pass exactly 2 or 4 arguments. With 3 arguments the third is ignored and the call appends. Numbers and other values are converted to text.
Parameters
-
fileNamestring - File to write, relative to the workspace
-
contentstring - Text to write
-
appendboolean optional - true to append to the existing content, false to replace the file
-
newLineboolean optional - Only used when appending to a file that already has content: true to put a line break before the text
Returns
Example runs in CI
Fl.write("log.txt", "first")Fl.write("log.txt", "second") -- appended on a new lineFl.write("log.txt", "only line", false, false) -- replaces the contentprint(#Fl.read("log.txt"))