Agent Filesystem
Each agent runs in a sandboxed filesystem. Both the /
path and the current working directory of an agent point to the root of this sandboxed filesystem.
There is no way for an agent to access files outside its own filesystem.
Accessing the filesystem
For TypeScript agents, a limited subset of the node:fs
package is available to work with files.
The available functions are:
Initial File System
The Initial File System (IFS) refers to all files that are present in the agent filesystem before the agent is started. These can include configuration files, static assets and other things that you want to include with your agent.
The IFS is configured on the level of a component, meaning that all agents created from a given component + version will always start with the
same filesystem. To configure the IFS, include a files
section in your golem.yaml
If you are using profiles in your golem.yaml, you currently have to include the files section in each of your profiles when overriding.
components:
example:filesystem:
template: ts
files:
- sourcePath: ./files/foo.txt
targetPath: /foobar.txt
permissions: read-write
- sourcePath: ./files/bar.txt
targetPath: /bar.txt
permissions: read-only
After deploying the component, any new agents created will have the file /foobar.txt
(and ./foobar.txt
as the agent's initial current directory is the root) available to them.
The file /bar.txt
on the other hand is only available for reading. Trying to open the file for writing will fail with a language-dependent error.
Updating an agent
Updating an agent that uses IFS requires some special consideration depending on the update mode you choose:
- Automatic updates: When using automatic updates the old agent invocations are replayed on top of the new IFS. This means that the agent should produce exactly the same results and side effects as it did with the old IFS. For example, changing the format of a file will work without issues, but changing a file that gets read by the agent and returned to the user will likely lead to divergence. In such cases a manual update might be necessary.
- Manual updates: When using manual updates, you are responsible for saving and restoring the content of files in the agent filesystem. You can
use the
golem:api/save-snapshot
function to persist the files and later restore / migrate them usinggolem:api/load-snapshot
.
Externally accessing agent files
Previous Golem versions allowed defining file-server bindings in the API mapping to expose files on the agent filesystem via a REST API. This feature is not available in Golem 1.3, but will be re-introduced in the next major release in a different and improved form.
There is no built-in way to automatically expose files on the agent filesystem for the outside world, but it is easy to define an agent method and map that to a HTTP route in the API mapping.
For example, we can define a get
method like in the following example agent:
import * as fs from 'node:fs';
import { BaseAgent, agent } from '@golemcloud/golem-ts-sdk';
@agent()
class FileServerAgent extends BaseAgent {
constructor(requestId: string) {
super();
}
get(filepath: string): { contentType: string, data: Uint8Array } {
try {
const buffer = fs.readFileSync("/web/" + filepath);
let contentType = "application/octet-stream";
if (filepath.endsWith(".html")) {
contentType = "text/html";
} else if (filepath.endsWith(".js")) {
contentType = "application/javascript";
}
return {
contentType: contentType,
data: buffer
};
} catch (err) {
return {
contentType: "text/plain",
data: new TextEncoder().encode(`Not found! (${ err })`)
};
}
}
}
Then in the HTTP API mapping we can map this method to a HTTP route:
httpApi:
definitions:
counter-api:
version: '0.0.2'
routes:
- method: POST
path: /{name}/increment
binding:
type: default
componentName: "example:counter"
response: |
let fsa = file-server-agent(request.request_id.value);
let file = fsa.get(request.path.file);
{
headers: {
Content-Type: file.content-type
},
body: file.data
}
For more information about defining custom HTTP APIs for agents, check the dedicated documentation page.
For serving static content, it is good practice to define a separate agent in an ephemeral component that is only responsible for serving files, and pass the request.request_id.value
string to its constructor to make sure each request gets its own unique ephemeral instance. This way the file-serving endpoints will not ever block by waiting for a stateful agent to process their requests.