Docs

Run agent tools remotely

In this example, we are going to let a hosted agent read and write customer files without giving the agent a customer cloud credential. The agent loop stays in your product; a Worker runs two reviewed tools against Storage in the selected customer deployment.

This pattern is useful when an agent needs access to data or services in a customer's environment. Your product invokes a named Command, the Worker performs that operation locally, and Alien returns the result.

The agent in your product invokes a named operation. A Worker in the customer environment runs the selected tool against private storage and returns the result.

We will create the Worker and Storage in alien.ts, implement the two tools in src/index.ts, and expose them as Commands. The Worker accepts this fixed tool list rather than arbitrary shell commands.

Describe the remote piece in alien.ts

alien.ts
const files = new alien.Storage("files").build()

const worker = new alien.Worker("worker")
  .code({ type: "source", src: "./", toolchain: { type: "typescript" } })
  .commandsEnabled(true)
  .link(files)
  .permissions("execution")
  .build()

.link(files) gives this Worker a binding to files; it does not publish the bucket or inject an AWS, GCP, or Azure credential into your code. The permission profile limits the Worker to the storage actions declared by the stack.

Write the tools

src/index.ts
const tools = {
  "read-file": {
    execute: async ({ path }: { path: string }) => {
      const object = await storage("files").get(path)
      return { content: new TextDecoder().decode(object.data) }
    },
  },
  "write-file": {
    execute: async ({ path, content }: { path: string; content: string }) => {
      await storage("files").put(path, new TextEncoder().encode(content))
      return { written: true, path }
    },
  },
}

The Worker exposes this fixed list, not a shell. Adding another tool is a code change you can review.

Make the tools callable

src/index.ts
command("execute-tool", toolSchema, async ({ tool, params }) => {
  const handler = tools[tool]
  if (!handler) throw new Error(`Unknown tool: ${tool}`)
  return handler.execute(params)
})

command("list-tools", async () => Object.keys(tools))

Run it locally

alien init remote-worker-ts
cd remote-worker-ts
alien dev
alien dev commands invoke --deployment default --command list-tools

Start with list-tools, then invoke execute-tool with read-file or write-file. Each local deployment gets its own Storage resource.

Put the tools beside customer data

1 · Release
alien release

Publishes a version. Nothing is deployed for a customer yet.

2 · Invite
alien onboard acme-corp

Creates a deployment link for that customer.

3 · Deploy

The customer opens the link and deploys into their environment.

Your hosted agent can now invoke these Commands against that deployment. The Worker reads and writes the customer's Storage; the agent receives only each tool result.

What you built

You kept the agent loop in your product and moved only two reviewed tools beside customer-owned Storage. This is the core remote-tools pattern: send a small operation to the data instead of giving the hosted agent direct storage credentials or copying the entire dataset back.

Source: examples/remote-worker-ts.

Next: Commands, Remote Bindings.

On this page