rivetkit 2.3.26 → 2.3.28

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.
Files changed (84) hide show
  1. package/dist/browser/client.js +2 -5
  2. package/dist/browser/client.js.map +1 -1
  3. package/dist/browser/inspector/client.js +2 -5
  4. package/dist/browser/inspector/client.js.map +1 -1
  5. package/dist/tsup/agent-os/index.cjs +3 -2652
  6. package/dist/tsup/agent-os/index.cjs.map +1 -1
  7. package/dist/tsup/agent-os/index.d.cts +1 -2717
  8. package/dist/tsup/agent-os/index.d.ts +1 -2717
  9. package/dist/tsup/agent-os/index.js +3 -2652
  10. package/dist/tsup/agent-os/index.js.map +1 -1
  11. package/dist/tsup/{chunk-LEXBAJ2Q.js → chunk-3EYXDC64.js} +3 -3
  12. package/dist/tsup/chunk-3EYXDC64.js.map +1 -0
  13. package/dist/tsup/{chunk-4SGTACEH.cjs → chunk-3X3QZVH6.cjs} +140 -140
  14. package/dist/tsup/chunk-3X3QZVH6.cjs.map +1 -0
  15. package/dist/tsup/{chunk-A27ATZJ4.cjs → chunk-4LMR7FES.cjs} +7 -7
  16. package/dist/tsup/{chunk-A27ATZJ4.cjs.map → chunk-4LMR7FES.cjs.map} +1 -1
  17. package/dist/tsup/{chunk-ZRCMI2TE.cjs → chunk-67PPGBOA.cjs} +3 -3
  18. package/dist/tsup/{chunk-ZRCMI2TE.cjs.map → chunk-67PPGBOA.cjs.map} +1 -1
  19. package/dist/tsup/{chunk-W5ADMRWL.cjs → chunk-DRG3YQ42.cjs} +3 -3
  20. package/dist/tsup/{chunk-W5ADMRWL.cjs.map → chunk-DRG3YQ42.cjs.map} +1 -1
  21. package/dist/tsup/{chunk-EQODJ4G7.cjs → chunk-FHZZ6PLK.cjs} +3 -6
  22. package/dist/tsup/chunk-FHZZ6PLK.cjs.map +1 -0
  23. package/dist/tsup/{chunk-NHXI4GVK.js → chunk-IKMLO5KS.js} +4 -4
  24. package/dist/tsup/{chunk-YXVLQDDK.js → chunk-KJBV7M2J.js} +3 -3
  25. package/dist/tsup/{chunk-7HU3OZCW.js → chunk-QLIVFNLT.js} +2 -2
  26. package/dist/tsup/{chunk-ORMO5OOW.cjs → chunk-S5EZP52Q.cjs} +3 -3
  27. package/dist/tsup/{chunk-ORMO5OOW.cjs.map → chunk-S5EZP52Q.cjs.map} +1 -1
  28. package/dist/tsup/{chunk-WI2Q7NT3.cjs → chunk-SRRUW63T.cjs} +4 -4
  29. package/dist/tsup/{chunk-WI2Q7NT3.cjs.map → chunk-SRRUW63T.cjs.map} +1 -1
  30. package/dist/tsup/{chunk-BKUZYZRZ.js → chunk-SWESH3VP.js} +2 -2
  31. package/dist/tsup/{chunk-XKVT36WN.js → chunk-UEQ5NN24.js} +2 -2
  32. package/dist/tsup/{chunk-3CZAZIO5.js → chunk-WB3BJJWJ.js} +3 -6
  33. package/dist/tsup/chunk-WB3BJJWJ.js.map +1 -0
  34. package/dist/tsup/{chunk-6AU5SM6I.cjs → chunk-XQIPFU3I.cjs} +10 -10
  35. package/dist/tsup/{chunk-6AU5SM6I.cjs.map → chunk-XQIPFU3I.cjs.map} +1 -1
  36. package/dist/tsup/{chunk-YQ7QHMPY.js → chunk-XQVCT3CM.js} +2 -2
  37. package/dist/tsup/client/mod.cjs +5 -5
  38. package/dist/tsup/client/mod.js +4 -4
  39. package/dist/tsup/common/log.cjs +2 -2
  40. package/dist/tsup/common/log.js +1 -1
  41. package/dist/tsup/common/websocket.cjs +3 -3
  42. package/dist/tsup/common/websocket.js +2 -2
  43. package/dist/tsup/db/drizzle.cjs +2 -2
  44. package/dist/tsup/db/drizzle.js +1 -1
  45. package/dist/tsup/db/mod.cjs +3 -3
  46. package/dist/tsup/db/mod.js +2 -2
  47. package/dist/tsup/inspector/mod.cjs +5 -5
  48. package/dist/tsup/inspector/mod.js +4 -4
  49. package/dist/tsup/inspector/workflow.cjs +4 -4
  50. package/dist/tsup/inspector/workflow.js +3 -3
  51. package/dist/tsup/mod.cjs +84 -84
  52. package/dist/tsup/mod.js +5 -5
  53. package/dist/tsup/test/mod.cjs +6 -6
  54. package/dist/tsup/test/mod.js +4 -4
  55. package/dist/tsup/utils.cjs +2 -2
  56. package/dist/tsup/utils.js +1 -1
  57. package/dist/tsup/workflow/mod.cjs +7 -7
  58. package/dist/tsup/workflow/mod.js +4 -4
  59. package/package.json +10 -13
  60. package/src/agent-os/index.ts +5 -65
  61. package/src/client/actor-handle.ts +3 -1
  62. package/src/client/client.ts +8 -4
  63. package/dist/tsup/chunk-3CZAZIO5.js.map +0 -1
  64. package/dist/tsup/chunk-4SGTACEH.cjs.map +0 -1
  65. package/dist/tsup/chunk-EQODJ4G7.cjs.map +0 -1
  66. package/dist/tsup/chunk-LEXBAJ2Q.js.map +0 -1
  67. package/src/agent-os/actor/cron.ts +0 -84
  68. package/src/agent-os/actor/db.ts +0 -54
  69. package/src/agent-os/actor/filesystem.ts +0 -131
  70. package/src/agent-os/actor/index.ts +0 -285
  71. package/src/agent-os/actor/network.ts +0 -58
  72. package/src/agent-os/actor/preview.ts +0 -190
  73. package/src/agent-os/actor/process.ts +0 -177
  74. package/src/agent-os/actor/session.ts +0 -518
  75. package/src/agent-os/actor/shell.ts +0 -60
  76. package/src/agent-os/config.ts +0 -79
  77. package/src/agent-os/fs/database-vfs.ts +0 -624
  78. package/src/agent-os/types.ts +0 -148
  79. /package/dist/tsup/{chunk-NHXI4GVK.js.map → chunk-IKMLO5KS.js.map} +0 -0
  80. /package/dist/tsup/{chunk-YXVLQDDK.js.map → chunk-KJBV7M2J.js.map} +0 -0
  81. /package/dist/tsup/{chunk-7HU3OZCW.js.map → chunk-QLIVFNLT.js.map} +0 -0
  82. /package/dist/tsup/{chunk-BKUZYZRZ.js.map → chunk-SWESH3VP.js.map} +0 -0
  83. /package/dist/tsup/{chunk-XKVT36WN.js.map → chunk-UEQ5NN24.js.map} +0 -0
  84. /package/dist/tsup/{chunk-YQ7QHMPY.js.map → chunk-XQVCT3CM.js.map} +0 -0
@@ -1,624 +0,0 @@
1
- /**
2
- * SQLite-backed VirtualFileSystem implementation.
3
- *
4
- * Stores file content, metadata (mode, timestamps), and directory structure
5
- * in SQLite tables managed by the RivetKit actor's database. This allows VM
6
- * filesystem state to persist across sleep/wake cycles.
7
- *
8
- * We use SQLite instead of actor KV because SQLite handles bulk write
9
- * optimizations (transactions, WAL mode, page caching) under the hood. With KV
10
- * we would need to manually chunk writes to stay under batch size limits,
11
- * implement our own indexing for directory listing queries, and handle
12
- * consistency across multiple KV operations. SQLite gives us all of this for
13
- * free.
14
- *
15
- * All paths are normalized to POSIX form (forward slashes, rooted at "/").
16
- */
17
-
18
- import * as posixPath from "node:path/posix";
19
- import type { RawAccess } from "@/common/database/config";
20
-
21
- // Infer VirtualFileSystem from PlainMountConfig.driver since
22
- // @secure-exec/core is not a direct dependency of this package.
23
- type VirtualFileSystem =
24
- import("@rivet-dev/agent-os-core").PlainMountConfig["driver"];
25
-
26
- // Infer VirtualStat from AgentOs.stat() return type.
27
- type VirtualStat = Awaited<
28
- ReturnType<import("@rivet-dev/agent-os-core").AgentOs["stat"]>
29
- >;
30
-
31
- // Infer VirtualDirEntry from readDirWithTypes.
32
- // VirtualDirEntry has: name, isDirectory, isSymbolicLink?, ino?
33
- interface VirtualDirEntry {
34
- name: string;
35
- isDirectory: boolean;
36
- isSymbolicLink?: boolean;
37
- ino?: number;
38
- }
39
-
40
- // POSIX mode constants.
41
- const S_IFDIR = 0o040000;
42
- const S_IFREG = 0o100000;
43
- const S_IFLNK = 0o120000;
44
- const DEFAULT_FILE_MODE = S_IFREG | 0o644;
45
- const DEFAULT_DIR_MODE = S_IFDIR | 0o755;
46
-
47
- interface FsRow extends Record<string, unknown> {
48
- path: string;
49
- is_directory: number;
50
- content: Uint8Array | null;
51
- mode: number;
52
- uid: number;
53
- gid: number;
54
- size: number;
55
- atime_ms: number;
56
- mtime_ms: number;
57
- ctime_ms: number;
58
- birthtime_ms: number;
59
- symlink_target: string | null;
60
- nlink: number;
61
- }
62
-
63
- function normPath(p: string): string {
64
- const normalized = posixPath.normalize(`/${p}`);
65
- // Remove trailing slash unless it's the root.
66
- if (normalized.length > 1 && normalized.endsWith("/")) {
67
- return normalized.slice(0, -1);
68
- }
69
- return normalized;
70
- }
71
-
72
- function parentPath(p: string): string {
73
- const parent = posixPath.dirname(p);
74
- return parent;
75
- }
76
-
77
- function throwENOENT(path: string): never {
78
- const err = new Error(`ENOENT: no such file or directory: ${path}`);
79
- err.name = "ENOENT";
80
- throw err;
81
- }
82
-
83
- function throwEEXIST(path: string): never {
84
- const err = new Error(`EEXIST: file already exists: ${path}`);
85
- err.name = "EEXIST";
86
- throw err;
87
- }
88
-
89
- function throwENOTDIR(path: string): never {
90
- const err = new Error(`ENOTDIR: not a directory: ${path}`);
91
- err.name = "ENOTDIR";
92
- throw err;
93
- }
94
-
95
- function throwEISDIR(path: string): never {
96
- const err = new Error(`EISDIR: illegal operation on a directory: ${path}`);
97
- err.name = "EISDIR";
98
- throw err;
99
- }
100
-
101
- function throwENOTEMPTY(path: string): never {
102
- const err = new Error(`ENOTEMPTY: directory not empty: ${path}`);
103
- err.name = "ENOTEMPTY";
104
- throw err;
105
- }
106
-
107
- function throwENOSYS(op: string): never {
108
- const err = new Error(`ENOSYS: function not implemented: ${op}`);
109
- err.name = "ENOSYS";
110
- throw err;
111
- }
112
-
113
- function rowToStat(row: FsRow): VirtualStat {
114
- return {
115
- mode: row.mode,
116
- size: row.size,
117
- isDirectory: row.is_directory === 1,
118
- isSymbolicLink: row.symlink_target !== null,
119
- atimeMs: row.atime_ms,
120
- mtimeMs: row.mtime_ms,
121
- ctimeMs: row.ctime_ms,
122
- birthtimeMs: row.birthtime_ms,
123
- ino: 0,
124
- nlink: row.nlink,
125
- uid: row.uid,
126
- gid: row.gid,
127
- };
128
- }
129
-
130
- export interface DatabaseVfsOptions {
131
- /** The RawAccess database handle from the actor's db provider. */
132
- db: RawAccess;
133
- }
134
-
135
- /**
136
- * Create a VirtualFileSystem backed by SQLite.
137
- *
138
- * The returned filesystem stores all content and metadata in the
139
- * `agent_os_fs_entries` table. The table must be created beforehand
140
- * via `migrateAgentOsTables()`.
141
- */
142
- export function createDatabaseVfs(
143
- options: DatabaseVfsOptions,
144
- ): VirtualFileSystem {
145
- const { db } = options;
146
-
147
- async function getEntry(path: string): Promise<FsRow | undefined> {
148
- const rows = await db.execute<FsRow>(
149
- "SELECT * FROM agent_os_fs_entries WHERE path = ?",
150
- path,
151
- );
152
- return rows[0];
153
- }
154
-
155
- async function getEntryOrThrow(path: string): Promise<FsRow> {
156
- const entry = await getEntry(path);
157
- if (!entry) {
158
- throwENOENT(path);
159
- }
160
- return entry;
161
- }
162
-
163
- async function ensureParentExists(path: string): Promise<void> {
164
- const parent = parentPath(path);
165
- if (parent === path) return; // root
166
- const entry = await getEntry(parent);
167
- if (!entry) {
168
- throwENOENT(parent);
169
- }
170
- if (entry.is_directory !== 1) {
171
- throwENOTDIR(parent);
172
- }
173
- }
174
-
175
- async function getChildEntries(dirPath: string): Promise<FsRow[]> {
176
- // Find direct children by matching paths that are one level deeper.
177
- // A direct child of "/foo" has path like "/foo/bar" but NOT "/foo/bar/baz".
178
- const prefix = dirPath === "/" ? "/" : `${dirPath}/`;
179
- const rows = await db.execute<FsRow>(
180
- "SELECT * FROM agent_os_fs_entries WHERE path LIKE ? AND path != ?",
181
- `${prefix}%`,
182
- dirPath,
183
- );
184
- // Filter to direct children only.
185
- return rows.filter((row) => {
186
- const relative = row.path.slice(prefix.length);
187
- return relative.length > 0 && !relative.includes("/");
188
- });
189
- }
190
-
191
- // Ensure root directory exists.
192
- const rootInit = (async () => {
193
- const root = await getEntry("/");
194
- if (!root) {
195
- const now = Date.now();
196
- await db.execute(
197
- `INSERT OR IGNORE INTO agent_os_fs_entries (path, is_directory, content, mode, uid, gid, size, atime_ms, mtime_ms, ctime_ms, birthtime_ms, symlink_target, nlink) VALUES (?, 1, NULL, ?, 0, 0, 0, ?, ?, ?, ?, NULL, 2)`,
198
- "/",
199
- DEFAULT_DIR_MODE,
200
- now,
201
- now,
202
- now,
203
- now,
204
- );
205
- }
206
- })();
207
-
208
- const backend: VirtualFileSystem = {
209
- async readFile(p: string): Promise<Uint8Array> {
210
- await rootInit;
211
- const path = normPath(p);
212
- const entry = await getEntryOrThrow(path);
213
- if (entry.is_directory === 1) {
214
- throwEISDIR(path);
215
- }
216
- return entry.content ?? new Uint8Array(0);
217
- },
218
-
219
- async readTextFile(p: string): Promise<string> {
220
- const data = await backend.readFile(p);
221
- return new TextDecoder().decode(data);
222
- },
223
-
224
- async readDir(p: string): Promise<string[]> {
225
- await rootInit;
226
- const path = normPath(p);
227
- const entry = await getEntryOrThrow(path);
228
- if (entry.is_directory !== 1) {
229
- throwENOTDIR(path);
230
- }
231
- const children = await getChildEntries(path);
232
- return children.map((child) => posixPath.basename(child.path));
233
- },
234
-
235
- async readDirWithTypes(p: string): Promise<VirtualDirEntry[]> {
236
- await rootInit;
237
- const path = normPath(p);
238
- const entry = await getEntryOrThrow(path);
239
- if (entry.is_directory !== 1) {
240
- throwENOTDIR(path);
241
- }
242
- const children = await getChildEntries(path);
243
- return children.map((child) => ({
244
- name: posixPath.basename(child.path),
245
- isDirectory: child.is_directory === 1,
246
- isSymbolicLink: child.symlink_target !== null,
247
- ino: 0,
248
- }));
249
- },
250
-
251
- async writeFile(
252
- p: string,
253
- content: string | Uint8Array,
254
- ): Promise<void> {
255
- await rootInit;
256
- const path = normPath(p);
257
- await ensureParentExists(path);
258
-
259
- const existing = await getEntry(path);
260
- if (existing && existing.is_directory === 1) {
261
- throwEISDIR(path);
262
- }
263
-
264
- const data =
265
- typeof content === "string"
266
- ? new TextEncoder().encode(content)
267
- : content;
268
- const now = Date.now();
269
-
270
- if (existing) {
271
- await db.execute(
272
- `UPDATE agent_os_fs_entries SET content = ?, size = ?, mtime_ms = ?, ctime_ms = ?, atime_ms = ? WHERE path = ?`,
273
- data,
274
- data.byteLength,
275
- now,
276
- now,
277
- now,
278
- path,
279
- );
280
- } else {
281
- await db.execute(
282
- `INSERT INTO agent_os_fs_entries (path, is_directory, content, mode, uid, gid, size, atime_ms, mtime_ms, ctime_ms, birthtime_ms, symlink_target, nlink) VALUES (?, 0, ?, ?, 0, 0, ?, ?, ?, ?, ?, NULL, 1)`,
283
- path,
284
- data,
285
- DEFAULT_FILE_MODE,
286
- data.byteLength,
287
- now,
288
- now,
289
- now,
290
- now,
291
- );
292
- }
293
- },
294
-
295
- async createDir(p: string): Promise<void> {
296
- await rootInit;
297
- const path = normPath(p);
298
- await ensureParentExists(path);
299
-
300
- const existing = await getEntry(path);
301
- if (existing) {
302
- throwEEXIST(path);
303
- }
304
-
305
- const now = Date.now();
306
- await db.execute(
307
- `INSERT INTO agent_os_fs_entries (path, is_directory, content, mode, uid, gid, size, atime_ms, mtime_ms, ctime_ms, birthtime_ms, symlink_target, nlink) VALUES (?, 1, NULL, ?, 0, 0, 0, ?, ?, ?, ?, NULL, 2)`,
308
- path,
309
- DEFAULT_DIR_MODE,
310
- now,
311
- now,
312
- now,
313
- now,
314
- );
315
- },
316
-
317
- async mkdir(
318
- p: string,
319
- options?: { recursive?: boolean },
320
- ): Promise<void> {
321
- await rootInit;
322
- const path = normPath(p);
323
-
324
- if (options?.recursive) {
325
- const parts = path.split("/").filter(Boolean);
326
- let current = "";
327
- for (const part of parts) {
328
- current += `/${part}`;
329
- const existing = await getEntry(current);
330
- if (!existing) {
331
- const now = Date.now();
332
- await db.execute(
333
- `INSERT INTO agent_os_fs_entries (path, is_directory, content, mode, uid, gid, size, atime_ms, mtime_ms, ctime_ms, birthtime_ms, symlink_target, nlink) VALUES (?, 1, NULL, ?, 0, 0, 0, ?, ?, ?, ?, NULL, 2)`,
334
- current,
335
- DEFAULT_DIR_MODE,
336
- now,
337
- now,
338
- now,
339
- now,
340
- );
341
- } else if (existing.is_directory !== 1) {
342
- throwENOTDIR(current);
343
- }
344
- }
345
- } else {
346
- await backend.createDir(p);
347
- }
348
- },
349
-
350
- async exists(p: string): Promise<boolean> {
351
- await rootInit;
352
- const path = normPath(p);
353
- const entry = await getEntry(path);
354
- return entry !== undefined;
355
- },
356
-
357
- async stat(p: string): Promise<VirtualStat> {
358
- await rootInit;
359
- const path = normPath(p);
360
- const entry = await getEntryOrThrow(path);
361
- return rowToStat(entry);
362
- },
363
-
364
- async removeFile(p: string): Promise<void> {
365
- await rootInit;
366
- const path = normPath(p);
367
- const entry = await getEntryOrThrow(path);
368
- if (entry.is_directory === 1) {
369
- throwEISDIR(path);
370
- }
371
- await db.execute(
372
- "DELETE FROM agent_os_fs_entries WHERE path = ?",
373
- path,
374
- );
375
- },
376
-
377
- async removeDir(p: string): Promise<void> {
378
- await rootInit;
379
- const path = normPath(p);
380
- const entry = await getEntryOrThrow(path);
381
- if (entry.is_directory !== 1) {
382
- throwENOTDIR(path);
383
- }
384
- const children = await getChildEntries(path);
385
- if (children.length > 0) {
386
- throwENOTEMPTY(path);
387
- }
388
- await db.execute(
389
- "DELETE FROM agent_os_fs_entries WHERE path = ?",
390
- path,
391
- );
392
- },
393
-
394
- async rename(oldPath: string, newPath: string): Promise<void> {
395
- await rootInit;
396
- const from = normPath(oldPath);
397
- const to = normPath(newPath);
398
-
399
- const entry = await getEntryOrThrow(from);
400
- await ensureParentExists(to);
401
-
402
- // Remove destination if it exists (overwrite semantics).
403
- const destEntry = await getEntry(to);
404
- if (destEntry) {
405
- if (destEntry.is_directory === 1) {
406
- const children = await getChildEntries(to);
407
- if (children.length > 0) {
408
- throwENOTEMPTY(to);
409
- }
410
- }
411
- await db.execute(
412
- "DELETE FROM agent_os_fs_entries WHERE path = ?",
413
- to,
414
- );
415
- }
416
-
417
- if (entry.is_directory === 1) {
418
- // Move all descendants by updating path prefixes.
419
- const prefix = from === "/" ? "/" : `${from}/`;
420
- const newPrefix = to === "/" ? "/" : `${to}/`;
421
-
422
- // Get all descendants first, then update them.
423
- const descendants = await db.execute<FsRow>(
424
- "SELECT path FROM agent_os_fs_entries WHERE path LIKE ?",
425
- `${prefix}%`,
426
- );
427
-
428
- for (const desc of descendants) {
429
- const newDescPath =
430
- newPrefix + desc.path.slice(prefix.length);
431
- await db.execute(
432
- "UPDATE agent_os_fs_entries SET path = ? WHERE path = ?",
433
- newDescPath,
434
- desc.path,
435
- );
436
- }
437
- }
438
-
439
- // Update the entry itself.
440
- const now = Date.now();
441
- await db.execute(
442
- "UPDATE agent_os_fs_entries SET path = ?, ctime_ms = ? WHERE path = ?",
443
- to,
444
- now,
445
- from,
446
- );
447
- },
448
-
449
- async realpath(p: string): Promise<string> {
450
- await rootInit;
451
- const path = normPath(p);
452
- const entry = await getEntryOrThrow(path);
453
- if (entry.symlink_target !== null) {
454
- return normPath(entry.symlink_target);
455
- }
456
- return path;
457
- },
458
-
459
- async symlink(target: string, linkPath: string): Promise<void> {
460
- await rootInit;
461
- const link = normPath(linkPath);
462
- await ensureParentExists(link);
463
-
464
- const existing = await getEntry(link);
465
- if (existing) {
466
- throwEEXIST(link);
467
- }
468
-
469
- const now = Date.now();
470
- await db.execute(
471
- `INSERT INTO agent_os_fs_entries (path, is_directory, content, mode, uid, gid, size, atime_ms, mtime_ms, ctime_ms, birthtime_ms, symlink_target, nlink) VALUES (?, 0, NULL, ?, 0, 0, ?, ?, ?, ?, ?, ?, 1)`,
472
- link,
473
- S_IFLNK | 0o777,
474
- target.length,
475
- now,
476
- now,
477
- now,
478
- now,
479
- target,
480
- );
481
- },
482
-
483
- async readlink(p: string): Promise<string> {
484
- await rootInit;
485
- const path = normPath(p);
486
- const entry = await getEntryOrThrow(path);
487
- if (entry.symlink_target === null) {
488
- const err = new Error(`EINVAL: not a symlink: ${path}`);
489
- err.name = "EINVAL";
490
- throw err;
491
- }
492
- return entry.symlink_target;
493
- },
494
-
495
- async lstat(p: string): Promise<VirtualStat> {
496
- // lstat does not follow symlinks; same as stat for our storage model.
497
- return backend.stat(p);
498
- },
499
-
500
- async link(_oldPath: string, _newPath: string): Promise<void> {
501
- throwENOSYS("link");
502
- },
503
-
504
- async chmod(p: string, mode: number): Promise<void> {
505
- await rootInit;
506
- const path = normPath(p);
507
- await getEntryOrThrow(path);
508
- const now = Date.now();
509
- await db.execute(
510
- "UPDATE agent_os_fs_entries SET mode = ?, ctime_ms = ? WHERE path = ?",
511
- mode,
512
- now,
513
- path,
514
- );
515
- },
516
-
517
- async chown(p: string, uid: number, gid: number): Promise<void> {
518
- await rootInit;
519
- const path = normPath(p);
520
- await getEntryOrThrow(path);
521
- const now = Date.now();
522
- await db.execute(
523
- "UPDATE agent_os_fs_entries SET uid = ?, gid = ?, ctime_ms = ? WHERE path = ?",
524
- uid,
525
- gid,
526
- now,
527
- path,
528
- );
529
- },
530
-
531
- async utimes(p: string, atime: number, mtime: number): Promise<void> {
532
- await rootInit;
533
- const path = normPath(p);
534
- await getEntryOrThrow(path);
535
- const now = Date.now();
536
- await db.execute(
537
- "UPDATE agent_os_fs_entries SET atime_ms = ?, mtime_ms = ?, ctime_ms = ? WHERE path = ?",
538
- atime,
539
- mtime,
540
- now,
541
- path,
542
- );
543
- },
544
-
545
- async truncate(p: string, length: number): Promise<void> {
546
- await rootInit;
547
- const path = normPath(p);
548
- const entry = await getEntryOrThrow(path);
549
- if (entry.is_directory === 1) {
550
- throwEISDIR(path);
551
- }
552
-
553
- const existing = entry.content ?? new Uint8Array(0);
554
- let newContent: Uint8Array;
555
- if (length >= existing.byteLength) {
556
- // Extend with zeros.
557
- newContent = new Uint8Array(length);
558
- newContent.set(existing);
559
- } else {
560
- // Truncate.
561
- newContent = existing.slice(0, length);
562
- }
563
-
564
- const now = Date.now();
565
- await db.execute(
566
- "UPDATE agent_os_fs_entries SET content = ?, size = ?, mtime_ms = ?, ctime_ms = ? WHERE path = ?",
567
- newContent,
568
- length,
569
- now,
570
- now,
571
- path,
572
- );
573
- },
574
-
575
- async pread(
576
- p: string,
577
- offset: number,
578
- length: number,
579
- ): Promise<Uint8Array> {
580
- await rootInit;
581
- const path = normPath(p);
582
- const entry = await getEntryOrThrow(path);
583
- if (entry.is_directory === 1) {
584
- throwEISDIR(path);
585
- }
586
- const content = entry.content ?? new Uint8Array(0);
587
- const end = Math.min(offset + length, content.byteLength);
588
- if (offset >= content.byteLength) {
589
- return new Uint8Array(0);
590
- }
591
- return content.slice(offset, end);
592
- },
593
-
594
- async pwrite(
595
- p: string,
596
- offset: number,
597
- data: Uint8Array,
598
- ): Promise<void> {
599
- await rootInit;
600
- const path = normPath(p);
601
- const entry = await getEntryOrThrow(path);
602
- if (entry.is_directory === 1) {
603
- throwEISDIR(path);
604
- }
605
- const content = entry.content ?? new Uint8Array(0);
606
- const end = offset + data.byteLength;
607
- const newSize = Math.max(content.byteLength, end);
608
- const buf = new Uint8Array(newSize);
609
- buf.set(content);
610
- buf.set(data, offset);
611
- const now = Date.now();
612
- await db.execute(
613
- `UPDATE agent_os_fs_entries SET content = ?, size = ?, mtime_ms = ?, ctime_ms = ? WHERE path = ?`,
614
- buf,
615
- newSize,
616
- now,
617
- now,
618
- path,
619
- );
620
- },
621
- };
622
-
623
- return backend;
624
- }