agentfs-sdk 0.2.0-pre.2 → 0.2.0-pre.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/filesystem.d.ts +7 -2
- package/dist/filesystem.js +72 -31
- package/package.json +1 -1
package/dist/filesystem.d.ts
CHANGED
|
@@ -17,10 +17,15 @@ export declare class Filesystem {
|
|
|
17
17
|
private db;
|
|
18
18
|
private initialized;
|
|
19
19
|
private rootIno;
|
|
20
|
+
private chunkSize;
|
|
20
21
|
constructor(db: Database);
|
|
22
|
+
/**
|
|
23
|
+
* Get the configured chunk size
|
|
24
|
+
*/
|
|
25
|
+
getChunkSize(): number;
|
|
21
26
|
private initialize;
|
|
22
27
|
/**
|
|
23
|
-
* Ensure root directory
|
|
28
|
+
* Ensure config and root directory exist, returns the chunk_size
|
|
24
29
|
*/
|
|
25
30
|
private ensureRoot;
|
|
26
31
|
/**
|
|
@@ -57,7 +62,7 @@ export declare class Filesystem {
|
|
|
57
62
|
private getLinkCount;
|
|
58
63
|
writeFile(path: string, content: string | Buffer): Promise<void>;
|
|
59
64
|
private updateFileContent;
|
|
60
|
-
readFile(path: string): Promise<string>;
|
|
65
|
+
readFile(path: string, encoding?: BufferEncoding): Promise<Buffer | string>;
|
|
61
66
|
readdir(path: string): Promise<string[]>;
|
|
62
67
|
deleteFile(path: string): Promise<void>;
|
|
63
68
|
stat(path: string): Promise<Stats>;
|
package/dist/filesystem.js
CHANGED
|
@@ -9,12 +9,20 @@ const S_IFLNK = 0o120000; // Symbolic link
|
|
|
9
9
|
// Default permissions
|
|
10
10
|
const DEFAULT_FILE_MODE = S_IFREG | 0o644; // Regular file, rw-r--r--
|
|
11
11
|
const DEFAULT_DIR_MODE = S_IFDIR | 0o755; // Directory, rwxr-xr-x
|
|
12
|
+
const DEFAULT_CHUNK_SIZE = 4096;
|
|
12
13
|
class Filesystem {
|
|
13
14
|
constructor(db) {
|
|
14
15
|
this.rootIno = 1;
|
|
16
|
+
this.chunkSize = DEFAULT_CHUNK_SIZE;
|
|
15
17
|
this.db = db;
|
|
16
18
|
this.initialized = this.initialize();
|
|
17
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Get the configured chunk size
|
|
22
|
+
*/
|
|
23
|
+
getChunkSize() {
|
|
24
|
+
return this.chunkSize;
|
|
25
|
+
}
|
|
18
26
|
async initialize() {
|
|
19
27
|
// Ensure database is connected
|
|
20
28
|
try {
|
|
@@ -26,6 +34,13 @@ class Filesystem {
|
|
|
26
34
|
throw error;
|
|
27
35
|
}
|
|
28
36
|
}
|
|
37
|
+
// Create the config table
|
|
38
|
+
await this.db.exec(`
|
|
39
|
+
CREATE TABLE IF NOT EXISTS fs_config (
|
|
40
|
+
key TEXT PRIMARY KEY,
|
|
41
|
+
value TEXT NOT NULL
|
|
42
|
+
)
|
|
43
|
+
`);
|
|
29
44
|
// Create the inode table
|
|
30
45
|
await this.db.exec(`
|
|
31
46
|
CREATE TABLE IF NOT EXISTS fs_inode (
|
|
@@ -54,20 +69,14 @@ class Filesystem {
|
|
|
54
69
|
CREATE INDEX IF NOT EXISTS idx_fs_dentry_parent
|
|
55
70
|
ON fs_dentry(parent_ino, name)
|
|
56
71
|
`);
|
|
57
|
-
// Create the data
|
|
72
|
+
// Create the data chunks table
|
|
58
73
|
await this.db.exec(`
|
|
59
74
|
CREATE TABLE IF NOT EXISTS fs_data (
|
|
60
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
61
75
|
ino INTEGER NOT NULL,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
76
|
+
chunk_index INTEGER NOT NULL,
|
|
77
|
+
data BLOB NOT NULL,
|
|
78
|
+
PRIMARY KEY (ino, chunk_index)
|
|
65
79
|
)
|
|
66
|
-
`);
|
|
67
|
-
// Create index for efficient data block lookups
|
|
68
|
-
await this.db.exec(`
|
|
69
|
-
CREATE INDEX IF NOT EXISTS idx_fs_data_ino_offset
|
|
70
|
-
ON fs_data(ino, offset)
|
|
71
80
|
`);
|
|
72
81
|
// Create the symlink table
|
|
73
82
|
await this.db.exec(`
|
|
@@ -76,13 +85,28 @@ class Filesystem {
|
|
|
76
85
|
target TEXT NOT NULL
|
|
77
86
|
)
|
|
78
87
|
`);
|
|
79
|
-
//
|
|
80
|
-
await this.ensureRoot();
|
|
88
|
+
// Initialize config and root directory, and get chunk_size
|
|
89
|
+
this.chunkSize = await this.ensureRoot();
|
|
81
90
|
}
|
|
82
91
|
/**
|
|
83
|
-
* Ensure root directory
|
|
92
|
+
* Ensure config and root directory exist, returns the chunk_size
|
|
84
93
|
*/
|
|
85
94
|
async ensureRoot() {
|
|
95
|
+
// Ensure chunk_size config exists and get its value
|
|
96
|
+
const configStmt = this.db.prepare("SELECT value FROM fs_config WHERE key = 'chunk_size'");
|
|
97
|
+
const config = await configStmt.get();
|
|
98
|
+
let chunkSize;
|
|
99
|
+
if (!config) {
|
|
100
|
+
const insertConfigStmt = this.db.prepare(`
|
|
101
|
+
INSERT INTO fs_config (key, value) VALUES ('chunk_size', ?)
|
|
102
|
+
`);
|
|
103
|
+
await insertConfigStmt.run(DEFAULT_CHUNK_SIZE.toString());
|
|
104
|
+
chunkSize = DEFAULT_CHUNK_SIZE;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
chunkSize = parseInt(config.value, 10) || DEFAULT_CHUNK_SIZE;
|
|
108
|
+
}
|
|
109
|
+
// Ensure root directory exists
|
|
86
110
|
const stmt = this.db.prepare('SELECT ino FROM fs_inode WHERE ino = ?');
|
|
87
111
|
const root = await stmt.get(this.rootIno);
|
|
88
112
|
if (!root) {
|
|
@@ -93,6 +117,7 @@ class Filesystem {
|
|
|
93
117
|
`);
|
|
94
118
|
await insertStmt.run(this.rootIno, DEFAULT_DIR_MODE, now, now, now);
|
|
95
119
|
}
|
|
120
|
+
return chunkSize;
|
|
96
121
|
}
|
|
97
122
|
/**
|
|
98
123
|
* Normalize a path
|
|
@@ -240,15 +265,22 @@ class Filesystem {
|
|
|
240
265
|
async updateFileContent(ino, content) {
|
|
241
266
|
const buffer = typeof content === 'string' ? Buffer.from(content, 'utf-8') : content;
|
|
242
267
|
const now = Math.floor(Date.now() / 1000);
|
|
243
|
-
// Delete existing data
|
|
268
|
+
// Delete existing data chunks
|
|
244
269
|
const deleteStmt = this.db.prepare('DELETE FROM fs_data WHERE ino = ?');
|
|
245
270
|
await deleteStmt.run(ino);
|
|
246
|
-
// Write data in chunks
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
271
|
+
// Write data in chunks
|
|
272
|
+
if (buffer.length > 0) {
|
|
273
|
+
const stmt = this.db.prepare(`
|
|
274
|
+
INSERT INTO fs_data (ino, chunk_index, data)
|
|
275
|
+
VALUES (?, ?, ?)
|
|
276
|
+
`);
|
|
277
|
+
let chunkIndex = 0;
|
|
278
|
+
for (let offset = 0; offset < buffer.length; offset += this.chunkSize) {
|
|
279
|
+
const chunk = buffer.subarray(offset, Math.min(offset + this.chunkSize, buffer.length));
|
|
280
|
+
await stmt.run(ino, chunkIndex, chunk);
|
|
281
|
+
chunkIndex++;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
252
284
|
// Update inode size and mtime
|
|
253
285
|
const updateStmt = this.db.prepare(`
|
|
254
286
|
UPDATE fs_inode
|
|
@@ -257,30 +289,36 @@ class Filesystem {
|
|
|
257
289
|
`);
|
|
258
290
|
await updateStmt.run(buffer.length, now, ino);
|
|
259
291
|
}
|
|
260
|
-
async readFile(path) {
|
|
292
|
+
async readFile(path, encoding) {
|
|
261
293
|
await this.initialized;
|
|
262
294
|
const ino = await this.resolvePath(path);
|
|
263
295
|
if (ino === null) {
|
|
264
296
|
throw new Error(`ENOENT: no such file or directory, open '${path}'`);
|
|
265
297
|
}
|
|
266
|
-
// Get all data
|
|
298
|
+
// Get all data chunks
|
|
267
299
|
const stmt = this.db.prepare(`
|
|
268
300
|
SELECT data FROM fs_data
|
|
269
301
|
WHERE ino = ?
|
|
270
|
-
ORDER BY
|
|
302
|
+
ORDER BY chunk_index ASC
|
|
271
303
|
`);
|
|
272
304
|
const rows = await stmt.all(ino);
|
|
305
|
+
let combined;
|
|
273
306
|
if (rows.length === 0) {
|
|
274
|
-
|
|
307
|
+
combined = Buffer.alloc(0);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
// Concatenate all chunks
|
|
311
|
+
const buffers = rows.map(row => row.data);
|
|
312
|
+
combined = Buffer.concat(buffers);
|
|
275
313
|
}
|
|
276
|
-
// Concatenate all chunks
|
|
277
|
-
const buffers = rows.map(row => row.data);
|
|
278
|
-
const combined = Buffer.concat(buffers);
|
|
279
314
|
// Update atime
|
|
280
315
|
const now = Math.floor(Date.now() / 1000);
|
|
281
316
|
const updateStmt = this.db.prepare('UPDATE fs_inode SET atime = ? WHERE ino = ?');
|
|
282
317
|
await updateStmt.run(now, ino);
|
|
283
|
-
|
|
318
|
+
if (encoding) {
|
|
319
|
+
return combined.toString(encoding);
|
|
320
|
+
}
|
|
321
|
+
return combined;
|
|
284
322
|
}
|
|
285
323
|
async readdir(path) {
|
|
286
324
|
await this.initialized;
|
|
@@ -316,9 +354,12 @@ class Filesystem {
|
|
|
316
354
|
// Check if this was the last link to the inode
|
|
317
355
|
const linkCount = await this.getLinkCount(ino);
|
|
318
356
|
if (linkCount === 0) {
|
|
319
|
-
// Delete the inode
|
|
320
|
-
const
|
|
321
|
-
await
|
|
357
|
+
// Delete the inode
|
|
358
|
+
const deleteInodeStmt = this.db.prepare('DELETE FROM fs_inode WHERE ino = ?');
|
|
359
|
+
await deleteInodeStmt.run(ino);
|
|
360
|
+
// Delete all data chunks
|
|
361
|
+
const deleteDataStmt = this.db.prepare('DELETE FROM fs_data WHERE ino = ?');
|
|
362
|
+
await deleteDataStmt.run(ino);
|
|
322
363
|
}
|
|
323
364
|
}
|
|
324
365
|
async stat(path) {
|