Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/typescript.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,38 @@ jobs:
- name: Test
run: npm test

test-browser:
name: Test
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
node-version: [22]
defaults:
run:
working-directory: sdk/typescript
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
cache-dependency-path: sdk/typescript/package-lock.json

- name: Install dependencies
run: npm ci

- name: Build
run: npm run build

- name: Install playwright
run: npx playwright install

- name: Test
run: npm run test:browser

check:
name: Check
runs-on: ubuntu-latest
Expand Down
871 changes: 576 additions & 295 deletions sdk/typescript/package-lock.json

Large diffs are not rendered by default.

22 changes: 18 additions & 4 deletions sdk/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@
"name": "agentfs-sdk",
"version": "0.3.0-pre.8",
"description": "AgentFS SDK",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"main": "dist/index_node.js",
"types": "dist/index_node.d.ts",
"packageManager": "[email protected]",
"type": "module",
"scripts": {
"build": "tsc",
"watch": "tsc --watch",
"test": "vitest run",
"test:browser": "CI=1 vitest -c vitest.browser.config.ts --browser=chromium --run && CI=1 vitest -c vitest.browser.config.ts --browser=firefox --run",
"test:watch": "vitest watch",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage",
"prepublishOnly": "npm run build"
},
"exports": {
".": {
"node": "./dist/index_node.js",
"browser": "./dist/index_browser.js",
"default": "./dist/index_node.js"
}
},
"keywords": [
"ai",
"agent",
Expand All @@ -29,15 +38,20 @@
"url": "https://github.com/tursodatabase/agentfs"
},
"devDependencies": {
"@tursodatabase/database-wasm": "^0.4.0-pre.17",
"@types/node": "^20.0.0",
"@vitest/browser": "^4.0.16",
"@vitest/browser-playwright": "^4.0.16",
"@vitest/ui": "^4.0.1",
"typescript": "^5.3.0",
"vitest": "^4.0.1"
},
"dependencies": {
"@tursodatabase/database": "^0.4.0-pre.17"
"@tursodatabase/database": "^0.4.0-pre.17",
"@tursodatabase/database-common": "^0.4.0-pre.17",
"buffer": "^6.0.3"
},
"files": [
"dist"
]
}
}
54 changes: 54 additions & 0 deletions sdk/typescript/src/agentfs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { DatabasePromise } from '@tursodatabase/database-common';
import { KvStore } from './kvstore.js';
import { Filesystem } from './filesystem.js';
import { ToolCalls } from './toolcalls.js';

/**
* Configuration options for opening an AgentFS instance
*/
export interface AgentFSOptions {
/**
* Unique identifier for the agent.
* - If provided without `path`: Creates storage at `.agentfs/{id}.db`
* - If provided with `path`: Uses the specified path
*/
id?: string;
/**
* Explicit path to the database file.
* - If provided: Uses the specified path directly
* - Can be combined with `id`
*/
path?: string;
}

export class AgentFSCore {
private db: DatabasePromise;

public readonly kv: KvStore;
public readonly fs: Filesystem;
public readonly tools: ToolCalls;

/**
* Private constructor - use AgentFS.open() instead
*/
protected constructor(db: DatabasePromise, kv: KvStore, fs: Filesystem, tools: ToolCalls) {
this.db = db;
this.kv = kv;
this.fs = fs;
this.tools = tools;
}

/**
* Get the underlying Database instance
*/
getDatabase(): DatabasePromise {
return this.db;
}

/**
* Close the database connection
*/
async close(): Promise<void> {
await this.db.close();
}
}
18 changes: 10 additions & 8 deletions sdk/typescript/src/filesystem.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Database } from '@tursodatabase/database';
import type { DatabasePromise } from '@tursodatabase/database-common';

// File types for mode field
const S_IFMT = 0o170000; // File type mask
Expand Down Expand Up @@ -28,19 +28,21 @@ export interface Stats {
}

export class Filesystem {
private db: Database;
private db: DatabasePromise;
private bufferCtor: BufferConstructor;
private rootIno: number = 1;
private chunkSize: number = DEFAULT_CHUNK_SIZE;

private constructor(db: Database) {
private constructor(db: DatabasePromise, b: BufferConstructor) {
this.db = db;
this.bufferCtor = b;
}

/**
* Create a Filesystem from an existing database connection
*/
static async fromDatabase(db: Database): Promise<Filesystem> {
const fs = new Filesystem(db);
static async fromDatabase(db: DatabasePromise, b?: BufferConstructor): Promise<Filesystem> {
const fs = new Filesystem(db, b ?? Buffer);
await fs.initialize();
return fs;
}
Expand Down Expand Up @@ -319,7 +321,7 @@ export class Filesystem {
}

private async updateFileContent(ino: number, content: string | Buffer): Promise<void> {
const buffer = typeof content === 'string' ? Buffer.from(content, 'utf-8') : content;
const buffer = typeof content === 'string' ? this.bufferCtor.from(content, 'utf-8') : content;
const now = Math.floor(Date.now() / 1000);

// Delete existing data chunks
Expand Down Expand Up @@ -374,11 +376,11 @@ export class Filesystem {

let combined: Buffer;
if (rows.length === 0) {
combined = Buffer.alloc(0);
combined = this.bufferCtor.alloc(0);
} else {
// Concatenate all chunks
const buffers = rows.map(row => row.data);
combined = Buffer.concat(buffers);
combined = this.bufferCtor.concat(buffers);
}

// Update atime
Expand Down
122 changes: 0 additions & 122 deletions sdk/typescript/src/index.ts

This file was deleted.

24 changes: 24 additions & 0 deletions sdk/typescript/src/index_browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { AgentFSCore } from "./agentfs.js";
import { DatabasePromise } from "@tursodatabase/database-common";
import { KvStore } from "./kvstore.js";
import { Filesystem } from "./filesystem.js";
import { ToolCalls } from "./toolcalls.js";
import { Buffer } from "buffer";

export class AgentFS extends AgentFSCore {
static async openWith(db: DatabasePromise): Promise<AgentFSCore> {
const [kv, fs, tools] = await Promise.all([
KvStore.fromDatabase(db),
Filesystem.fromDatabase(db, Buffer),
ToolCalls.fromDatabase(db),
]);
return new AgentFS(db, kv, fs, tools);
}
}

export { AgentFSOptions } from './agentfs.js';
export { KvStore } from './kvstore.js';
export { Filesystem } from './filesystem.js';
export type { Stats } from './filesystem.js';
export { ToolCalls } from './toolcalls.js';
export type { ToolCall, ToolCallStats } from './toolcalls.js';
Loading