Skip to content

Latest commit

 

History

History

agent-shell

Folders and files

README.md

@tigrisdata/agent-shell

A virtual bash environment with a persistent filesystem backed by Tigris object storage, written in TypeScript and designed for AI agents.

AI agents produce artifacts — reports, data, configs, logs. These need to go somewhere durable, shareable, and globally accessible. @tigrisdata/agent-shell gives agents a familiar bash interface (cat, grep, sed, jq, awk, pipes, redirects) where every file operation is backed by a Tigris bucket.

  • Isolated — writes stay in-memory until you explicitly flush. No partial state leaks to storage.
  • Durable — flush persists files to Tigris, globally distributed.
  • Checkpointable — take snapshots of your storage at any point. Roll back if needed.
  • Forkable — create copy-on-write forks of a bucket for safe experimentation.
  • Shareable — generate presigned URLs for any stored file.

Built on just-bash for the shell engine and @tigrisdata/storage for the storage layer.

Quick Start

Programmatic Usage

npm install @tigrisdata/agent-shell
import { TigrisShell } from '@tigrisdata/agent-shell';

const shell = new TigrisShell({
  accessKeyId: process.env.TIGRIS_STORAGE_ACCESS_KEY_ID,
  secretAccessKey: process.env.TIGRIS_STORAGE_SECRET_ACCESS_KEY,
  bucket: process.env.TIGRIS_STORAGE_BUCKET, // optional — auto-mounts at /workspace
});

await shell.exec('echo "Hello world" > greeting.txt');
await shell.exec('cat greeting.txt'); // stdout: "Hello world\n"
await shell.exec('cat greeting.txt | tr a-z A-Z'); // stdout: "HELLO WORLD\n"

// Persist to Tigris when you're ready
await shell.flush();

Interactive Shell

Launch a shell directly — no install needed:

npx @tigrisdata/agent-shell

Authenticate with access keys:

$ configure --key tid_... --secret tsec_...
Mounted 2 bucket(s) at /. Run 'df' to list them.

/ $ ls
my-bucket  shared-data
/ $ cd my-bucket
/my-bucket $ echo "hello" > greeting.txt
/my-bucket $ cat greeting.txt
hello
/my-bucket $ flush
Flushed 2 mount(s)

Or login with your Tigris account:

$ login
Open this URL in your browser:
  https://auth.storage.tigrisdata.io/activate?user_code=XKCD-1234

Waiting for authorization... done!
Logged in as you@example.com

Mounted 2 bucket(s) at /. Run 'df' to list them.

You can also pass credentials as flags:

npx @tigrisdata/agent-shell --key tid_... --secret tsec_... --bucket my-bucket

Storage Model

The shell uses an in-memory write-back cache that provides isolation:

Agent writes file  →  cached in memory (isolated)
Agent reads file   →  cache hit or fetch from Tigris
Agent calls flush  →  all changes persisted atomically

This gives you:

  • Isolation — nothing touches storage until you say so
  • Atomic commits — if your agent fails midway, no partial state is written
  • Fast execution — most operations never hit the network
const shell = new TigrisShell({
  bucket: process.env.TIGRIS_STORAGE_BUCKET,
  accessKeyId: process.env.TIGRIS_STORAGE_ACCESS_KEY_ID,
  secretAccessKey: process.env.TIGRIS_STORAGE_SECRET_ACCESS_KEY,
});

try {
  await shell.exec('echo "processing..." > status.txt');
  await shell.exec('echo \'{"score": 0.95}\' > results.json');
  await shell.exec('cat results.json | jq .score'); // "0.95\n"

  // Only persist on success
  await shell.flush();
} catch (e) {
  // Nothing was written to Tigris — storage is clean
}

Shell Options

The second argument configures shell behavior:

const shell = new TigrisShell(
  {
    bucket: process.env.TIGRIS_STORAGE_BUCKET,
    accessKeyId: process.env.TIGRIS_STORAGE_ACCESS_KEY_ID,
    secretAccessKey: process.env.TIGRIS_STORAGE_SECRET_ACCESS_KEY,
  },
  {
    cwd: '/workspace', // Starting directory (default: /workspace)
    env: { DEBUG: 'true' }, // Initial environment variables
  }
);

Built-in Tigris Commands

In addition to all standard bash commands from just-bash, the shell includes Tigris-specific commands:

presign

Generate presigned URLs for sharing or uploading.

presign /path/to/file.txt                    # GET URL, 1 hour expiry
presign /path/to/file.txt --expires 7200     # GET URL, 2 hour expiry
presign /path/to/file.txt --put              # PUT URL for uploads
presign /path/to/file.txt --key tid_...      # Sign with this access key (required after 'login')

snapshot

Checkpoint your dataset. Create or list point-in-time bucket snapshots.

snapshot my-bucket                           # Create a snapshot
snapshot my-bucket --name "checkpoint-1"     # Create a named snapshot
snapshot my-bucket --list                    # List all snapshots

fork

Branch your dataset. Create a copy-on-write fork for safe experimentation, or list the forks of a bucket.

fork source-bucket --name my-fork            # Fork a bucket
fork source-bucket --name my-fork --snapshot 1713200000   # Fork from a specific snapshot
fork --name my-fork                          # Fork the bucket mounted at the current directory
fork source-bucket --list                    # List forks of a bucket

Multi-Bucket

Mount multiple buckets at different paths:

import { TigrisShell } from '@tigrisdata/agent-shell';

const shell = new TigrisShell({
  accessKeyId: process.env.TIGRIS_STORAGE_ACCESS_KEY_ID,
  secretAccessKey: process.env.TIGRIS_STORAGE_SECRET_ACCESS_KEY,
  bucket: 'agent-workspace', // auto-mounted at /workspace
});

shell.mount('shared-datasets', '/datasets');

await shell.exec('cat /datasets/training/labels.csv | head -10');
await shell.exec('cp /datasets/training/labels.csv ./local-copy.csv');
await shell.exec('echo "processed" > results.txt');

// Flush all mounts, or a specific one
await shell.flush(); // all
await shell.flush('/datasets'); // just /datasets

// List and unmount
shell.listMounts(); // [{ bucket: "agent-workspace", mountPoint: "/workspace" }, ...]
shell.unmount('/datasets');

Advanced: Compose with just-bash

For full control over the filesystem layout, install just-bash alongside agent-shell:

npm install @tigrisdata/agent-shell just-bash
import { Bash, MountableFs, InMemoryFs } from 'just-bash';
import { TigrisAdapter } from '@tigrisdata/agent-shell/fs';
import { createTigrisCommands } from '@tigrisdata/agent-shell/commands';

const auth = {
  accessKeyId: process.env.TIGRIS_STORAGE_ACCESS_KEY_ID,
  secretAccessKey: process.env.TIGRIS_STORAGE_SECRET_ACCESS_KEY,
};

const fs = new MountableFs({ base: new InMemoryFs() });
fs.mount('/workspace', new TigrisAdapter(auth, 'agent-workspace'));
fs.mount('/datasets', new TigrisAdapter(auth, 'shared-datasets'));

const bash = new Bash({
  fs,
  cwd: '/workspace',
  customCommands: [
    ...createTigrisCommands({ ...auth, bucket: 'agent-workspace' }),
  ],
});

await bash.exec('cp /datasets/data.csv ./local.csv');

API Reference

@tigrisdata/agent-shell

Export Description
TigrisShell Main class — persisted storage shell backed by Tigris
TigrisConfig Config type: { accessKeyId?, secretAccessKey?, sessionToken?, organizationId?, bucket?, endpoint?, forcePathStyle? }
ShellOptions Shell options type: { cwd?, env? }

TigrisShell

new TigrisShell(config: TigrisConfig, shellOptions?: ShellOptions)
Method Returns Description
exec(command) Promise<BashExecResult> Execute a bash command
mount(bucket, mountPoint) void Mount a bucket at a path
unmount(mountPoint) void Unmount a path
listMounts() Array<{ bucket, mountPoint }> List current mounts
flush(mountPoint?) Promise<void> Flush all mounts or a specific one
engine Bash Underlying just-bash instance

@tigrisdata/agent-shell/fs

Export Description
TigrisAdapter Filesystem adapter implementing just-bash's IFileSystem
TigrisConfig Same config type as main export

@tigrisdata/agent-shell/commands

Export Description
createTigrisCommands(config) Create all Tigris commands
createPresignCommand(config) Create presign command only
createSnapshotCommand(config) Create snapshot command only
createForkCommand(config) Create fork command only

Examples

All examples require TIGRIS_STORAGE_ACCESS_KEY_ID, TIGRIS_STORAGE_SECRET_ACCESS_KEY, and TIGRIS_STORAGE_BUCKET env vars. Run with npx tsx examples/<name>.ts.

Example Description
basic.ts Write files, pipe/process, flush to Tigris
presign.ts Generate shareable and upload URLs for stored files
snapshot-and-fork.ts Checkpoint and branch storage — Tigris-unique features
multi-bucket.ts Mount multiple buckets, copy across them (advanced)

Development

# Install dependencies (from the monorepo root)
pnpm install

# Type check
pnpm --filter @tigrisdata/agent-shell typecheck

# Lint and format
pnpm --filter @tigrisdata/agent-shell lint
pnpm --filter @tigrisdata/agent-shell lint:fix

# Run tests
pnpm --filter @tigrisdata/agent-shell test

# Build
pnpm --filter @tigrisdata/agent-shell build

License

MIT