@rivet-dev/agentos-core 0.0.0-abc-fixes-1.d8c4d95 → 0.0.0-agent-acp-codex-session-benchmark.d578a31
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/README.md +48 -48
- package/dist/agent-os.d.ts +215 -161
- package/dist/agent-os.js +1300 -2576
- package/dist/agentos-package.d.ts +27 -0
- package/dist/agentos-package.js +21 -0
- package/dist/base-filesystem.d.ts +1 -16
- package/dist/base-filesystem.js +1 -173
- package/dist/cron/cron-manager.d.ts +39 -14
- package/dist/cron/cron-manager.js +289 -128
- package/dist/cron/errors.d.ts +8 -0
- package/dist/cron/errors.js +16 -0
- package/dist/cron/index.d.ts +1 -3
- package/dist/cron/index.js +1 -2
- package/dist/cron/types.d.ts +2 -2
- package/dist/default-software.d.ts +8 -0
- package/dist/default-software.js +10 -0
- package/dist/filesystem-snapshot.d.ts +1 -1
- package/dist/filesystem-snapshot.js +1 -1
- package/dist/generated/AcpLimitsConfig.d.ts +4 -0
- package/dist/generated/AcpLimitsConfig.js +2 -0
- package/dist/generated/CreateVmConfig.d.ts +29 -0
- package/dist/generated/FsPermissionRule.d.ts +6 -0
- package/dist/generated/FsPermissionRule.js +1 -0
- package/dist/generated/FsPermissionRuleSet.d.ts +6 -0
- package/dist/generated/FsPermissionRuleSet.js +1 -0
- package/dist/generated/FsPermissionScope.d.ts +3 -0
- package/dist/generated/FsPermissionScope.js +1 -0
- package/dist/generated/HttpLimitsConfig.d.ts +3 -0
- package/dist/generated/HttpLimitsConfig.js +2 -0
- package/dist/generated/JsModuleResolution.d.ts +1 -0
- package/dist/generated/JsModuleResolution.js +2 -0
- package/dist/generated/JsRuntimeConfig.d.ts +31 -0
- package/dist/generated/JsRuntimeConfig.js +1 -0
- package/dist/generated/JsRuntimeLimitsConfig.d.ts +8 -0
- package/dist/generated/JsRuntimeLimitsConfig.js +2 -0
- package/dist/generated/JsRuntimePlatform.d.ts +1 -0
- package/dist/generated/JsRuntimePlatform.js +2 -0
- package/dist/generated/MountPluginDescriptor.d.ts +4 -0
- package/dist/generated/MountPluginDescriptor.js +2 -0
- package/dist/generated/NativeRootFilesystemConfig.d.ts +5 -0
- package/dist/generated/NativeRootFilesystemConfig.js +1 -0
- package/dist/generated/PatternPermissionRule.d.ts +6 -0
- package/dist/generated/PatternPermissionRule.js +1 -0
- package/dist/generated/PatternPermissionRuleSet.d.ts +6 -0
- package/dist/generated/PatternPermissionRuleSet.js +1 -0
- package/dist/generated/PatternPermissionScope.d.ts +3 -0
- package/dist/generated/PatternPermissionScope.js +1 -0
- package/dist/generated/PermissionMode.d.ts +1 -0
- package/dist/generated/PermissionMode.js +2 -0
- package/dist/generated/PermissionsPolicy.d.ts +10 -0
- package/dist/generated/PermissionsPolicy.js +1 -0
- package/dist/generated/PluginLimitsConfig.d.ts +4 -0
- package/dist/generated/PluginLimitsConfig.js +2 -0
- package/dist/generated/PythonLimitsConfig.d.ts +6 -0
- package/dist/generated/PythonLimitsConfig.js +2 -0
- package/dist/generated/ResourceLimitsConfig.d.ts +25 -0
- package/dist/generated/ResourceLimitsConfig.js +2 -0
- package/dist/generated/RootFilesystemConfig.d.ts +9 -0
- package/dist/generated/RootFilesystemConfig.js +1 -0
- package/dist/generated/RootFilesystemEntry.d.ts +13 -0
- package/dist/generated/RootFilesystemEntry.js +1 -0
- package/dist/generated/RootFilesystemEntryEncoding.d.ts +1 -0
- package/dist/generated/RootFilesystemEntryEncoding.js +2 -0
- package/dist/generated/RootFilesystemEntryKind.d.ts +1 -0
- package/dist/generated/RootFilesystemEntryKind.js +2 -0
- package/dist/generated/RootFilesystemLowerDescriptor.d.ts +7 -0
- package/dist/generated/RootFilesystemLowerDescriptor.js +1 -0
- package/dist/generated/RootFilesystemMode.d.ts +1 -0
- package/dist/generated/RootFilesystemMode.js +2 -0
- package/dist/generated/ToolLimitsConfig.d.ts +10 -0
- package/dist/generated/ToolLimitsConfig.js +2 -0
- package/dist/generated/VmDnsConfig.d.ts +6 -0
- package/dist/generated/VmDnsConfig.js +2 -0
- package/dist/generated/VmLimitsConfig.d.ts +18 -0
- package/dist/generated/VmLimitsConfig.js +1 -0
- package/dist/generated/VmListenPolicyConfig.d.ts +5 -0
- package/dist/generated/VmListenPolicyConfig.js +2 -0
- package/dist/generated/WasmLimitsConfig.d.ts +7 -0
- package/dist/generated/WasmLimitsConfig.js +2 -0
- package/dist/host-dir-mount.d.ts +5 -5
- package/dist/host-dir-mount.js +4 -4
- package/dist/host-tools-zod.js +27 -7
- package/dist/host-tools.d.ts +2 -9
- package/dist/host-tools.js +0 -18
- package/dist/index.d.ts +7 -4
- package/dist/index.js +6 -4
- package/dist/layers.d.ts +3 -3
- package/dist/layers.js +19 -10
- package/dist/memory-filesystem.d.ts +39 -0
- package/dist/memory-filesystem.js +375 -0
- package/dist/options-schema.d.ts +452 -0
- package/dist/options-schema.js +269 -0
- package/dist/overlay-filesystem.d.ts +1 -1
- package/dist/overlay-filesystem.js +3 -3
- package/dist/packages.d.ts +10 -123
- package/dist/packages.js +3 -329
- package/dist/runtime.d.ts +41 -184
- package/dist/sidecar/agentos-protocol.d.ts +232 -29
- package/dist/sidecar/agentos-protocol.js +456 -120
- package/dist/sidecar/binary.d.ts +2 -2
- package/dist/sidecar/binary.js +2 -2
- package/dist/sidecar/native-process-client.d.ts +5 -3
- package/dist/sidecar/native-process-client.js +5 -3
- package/dist/sidecar/permissions.d.ts +4 -3
- package/dist/sidecar/permissions.js +6 -23
- package/dist/sidecar/rpc-client.d.ts +74 -86
- package/dist/sidecar/rpc-client.js +422 -1418
- package/dist/test/file-system.d.ts +1 -1
- package/dist/test/file-system.js +3 -1
- package/dist/test/runtime.d.ts +2 -3
- package/dist/test/runtime.js +1 -1
- package/dist/test/terminal-harness.d.ts +5 -4
- package/dist/test/terminal-harness.js +12 -7
- package/dist/types.d.ts +12 -5
- package/package.json +37 -41
- package/dist/agents.d.ts +0 -63
- package/dist/agents.js +0 -42
- package/dist/cron/parse-schedule.d.ts +0 -25
- package/dist/cron/parse-schedule.js +0 -66
- package/dist/cron/schedule-driver.d.ts +0 -19
- package/dist/cron/timer-driver.d.ts +0 -17
- package/dist/cron/timer-driver.js +0 -61
- package/dist/runtime-compat.d.ts +0 -475
- package/dist/runtime-compat.js +0 -2010
- package/dist/sidecar/cargo.d.ts +0 -2
- package/dist/sidecar/cargo.js +0 -142
- package/fixtures/AGENTOS_SYSTEM_PROMPT.md +0 -21
- /package/dist/{cron/schedule-driver.js → generated/CreateVmConfig.js} +0 -0
package/dist/layers.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { getBaseFilesystemSnapshot, } from "./base-filesystem.js";
|
|
3
2
|
import { createFilesystemFromEntries, snapshotVirtualFilesystem, } from "./filesystem-snapshot.js";
|
|
4
3
|
import { createOverlayBackend } from "./overlay-filesystem.js";
|
|
5
4
|
function cloneSnapshotHandle(storeId, layerId) {
|
|
@@ -73,8 +72,11 @@ export function createInMemoryLayerStore() {
|
|
|
73
72
|
}
|
|
74
73
|
return state;
|
|
75
74
|
}
|
|
76
|
-
|
|
75
|
+
const store = {
|
|
77
76
|
storeId,
|
|
77
|
+
get retainedLayerCount() {
|
|
78
|
+
return layers.size;
|
|
79
|
+
},
|
|
78
80
|
async createWritableLayer() {
|
|
79
81
|
const layerId = randomUUID();
|
|
80
82
|
const leaseId = randomUUID();
|
|
@@ -117,10 +119,11 @@ export function createInMemoryLayerStore() {
|
|
|
117
119
|
if (state.kind !== "writable") {
|
|
118
120
|
throw new Error(`Layer ${layer.layerId} is not writable`);
|
|
119
121
|
}
|
|
120
|
-
|
|
122
|
+
const baseFs = state.activeOverlay ?? state.fs;
|
|
123
|
+
if (!state.valid || state.leaseId !== layer.leaseId || !baseFs) {
|
|
121
124
|
throw new Error(`Writable layer ${layer.layerId} is no longer valid`);
|
|
122
125
|
}
|
|
123
|
-
const entries = await snapshotVirtualFilesystem(
|
|
126
|
+
const entries = await snapshotVirtualFilesystem(baseFs);
|
|
124
127
|
const snapshot = createSnapshotExport(entries).source;
|
|
125
128
|
const layerId = randomUUID();
|
|
126
129
|
layers.set(layerId, {
|
|
@@ -128,8 +131,11 @@ export function createInMemoryLayerStore() {
|
|
|
128
131
|
snapshot,
|
|
129
132
|
fs: await createFilesystemFromEntries(snapshot.filesystem.entries),
|
|
130
133
|
});
|
|
134
|
+
// Release the writable layer's filesystem payload now that it is sealed;
|
|
135
|
+
// the invalid tombstone is retained so stale handles still report cleanly.
|
|
131
136
|
state.valid = false;
|
|
132
137
|
state.activeOverlay = null;
|
|
138
|
+
state.fs = null;
|
|
133
139
|
return cloneSnapshotHandle(storeId, layerId);
|
|
134
140
|
},
|
|
135
141
|
createOverlayFilesystem(options) {
|
|
@@ -162,6 +168,9 @@ export function createInMemoryLayerStore() {
|
|
|
162
168
|
if (upperState.activeOverlay) {
|
|
163
169
|
throw new Error(`Writable layer ${options.upper.layerId} is already attached to an overlay`);
|
|
164
170
|
}
|
|
171
|
+
if (!upperState.fs) {
|
|
172
|
+
throw new Error(`Writable layer ${options.upper.layerId} is no longer valid`);
|
|
173
|
+
}
|
|
165
174
|
const overlay = createOverlayBackend({
|
|
166
175
|
upper: upperState.fs,
|
|
167
176
|
lowers: lowers.map((lower) => {
|
|
@@ -175,11 +184,11 @@ export function createInMemoryLayerStore() {
|
|
|
175
184
|
upperState.activeOverlay = overlay;
|
|
176
185
|
return overlay;
|
|
177
186
|
},
|
|
187
|
+
dispose() {
|
|
188
|
+
// Release every retained layer's filesystem payload so the store does
|
|
189
|
+
// not accumulate snapshots/writable layers for the process lifetime.
|
|
190
|
+
layers.clear();
|
|
191
|
+
},
|
|
178
192
|
};
|
|
179
|
-
|
|
180
|
-
export function createDefaultRootLowerInput() {
|
|
181
|
-
return {
|
|
182
|
-
kind: "base-filesystem-artifact",
|
|
183
|
-
source: getBaseFilesystemSnapshot(),
|
|
184
|
-
};
|
|
193
|
+
return store;
|
|
185
194
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { VirtualDirEntry, VirtualFileSystem, VirtualStat } from "./runtime.js";
|
|
2
|
+
export declare class KernelError extends Error {
|
|
3
|
+
readonly code: string;
|
|
4
|
+
constructor(code: string, message: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class InMemoryFileSystem implements VirtualFileSystem {
|
|
7
|
+
private readonly entries;
|
|
8
|
+
constructor();
|
|
9
|
+
readFile(targetPath: string): Promise<Uint8Array>;
|
|
10
|
+
readTextFile(targetPath: string): Promise<string>;
|
|
11
|
+
readDir(targetPath: string): Promise<string[]>;
|
|
12
|
+
readDirWithTypes(targetPath: string): Promise<VirtualDirEntry[]>;
|
|
13
|
+
writeFile(targetPath: string, content: string | Uint8Array): Promise<void>;
|
|
14
|
+
createDir(targetPath: string): Promise<void>;
|
|
15
|
+
mkdir(targetPath: string, options?: {
|
|
16
|
+
recursive?: boolean;
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
exists(targetPath: string): Promise<boolean>;
|
|
19
|
+
stat(targetPath: string): Promise<VirtualStat>;
|
|
20
|
+
removeFile(targetPath: string): Promise<void>;
|
|
21
|
+
removeDir(targetPath: string): Promise<void>;
|
|
22
|
+
rename(oldPath: string, newPath: string): Promise<void>;
|
|
23
|
+
realpath(targetPath: string): Promise<string>;
|
|
24
|
+
symlink(target: string, linkPath: string): Promise<void>;
|
|
25
|
+
readlink(targetPath: string): Promise<string>;
|
|
26
|
+
lstat(targetPath: string): Promise<VirtualStat>;
|
|
27
|
+
link(oldPath: string, newPath: string): Promise<void>;
|
|
28
|
+
chmod(targetPath: string, mode: number): Promise<void>;
|
|
29
|
+
chown(targetPath: string, uid: number, gid: number): Promise<void>;
|
|
30
|
+
utimes(targetPath: string, atime: number, mtime: number): Promise<void>;
|
|
31
|
+
truncate(targetPath: string, length: number): Promise<void>;
|
|
32
|
+
pread(targetPath: string, offset: number, length: number): Promise<Uint8Array>;
|
|
33
|
+
pwrite(targetPath: string, offset: number, data: Uint8Array): Promise<void>;
|
|
34
|
+
private resolvePath;
|
|
35
|
+
private resolveEntry;
|
|
36
|
+
private newDirectory;
|
|
37
|
+
private toStat;
|
|
38
|
+
}
|
|
39
|
+
export declare function createInMemoryFileSystem(): InMemoryFileSystem;
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
const S_IFREG = 0o100000;
|
|
2
|
+
const S_IFDIR = 0o040000;
|
|
3
|
+
const S_IFLNK = 0o120000;
|
|
4
|
+
const MAX_SYMLINK_DEPTH = 40;
|
|
5
|
+
export class KernelError extends Error {
|
|
6
|
+
code;
|
|
7
|
+
constructor(code, message) {
|
|
8
|
+
super(message.startsWith(`${code}:`) ? message : `${code}: ${message}`);
|
|
9
|
+
this.name = "KernelError";
|
|
10
|
+
this.code = code;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function normalizePath(inputPath) {
|
|
14
|
+
if (!inputPath)
|
|
15
|
+
return "/";
|
|
16
|
+
let normalized = inputPath.startsWith("/") ? inputPath : `/${inputPath}`;
|
|
17
|
+
normalized = normalized.replace(/\/+/g, "/");
|
|
18
|
+
if (normalized.length > 1 && normalized.endsWith("/")) {
|
|
19
|
+
normalized = normalized.slice(0, -1);
|
|
20
|
+
}
|
|
21
|
+
const parts = normalized.split("/");
|
|
22
|
+
const resolved = [];
|
|
23
|
+
for (const part of parts) {
|
|
24
|
+
if (part === "" || part === ".")
|
|
25
|
+
continue;
|
|
26
|
+
if (part === "..") {
|
|
27
|
+
resolved.pop();
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
resolved.push(part);
|
|
31
|
+
}
|
|
32
|
+
return resolved.length === 0 ? "/" : `/${resolved.join("/")}`;
|
|
33
|
+
}
|
|
34
|
+
function dirnameVirtual(inputPath) {
|
|
35
|
+
const normalized = normalizePath(inputPath);
|
|
36
|
+
if (normalized === "/")
|
|
37
|
+
return "/";
|
|
38
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
39
|
+
return parts.length <= 1 ? "/" : `/${parts.slice(0, -1).join("/")}`;
|
|
40
|
+
}
|
|
41
|
+
let nextInode = 1;
|
|
42
|
+
export class InMemoryFileSystem {
|
|
43
|
+
entries = new Map();
|
|
44
|
+
constructor() {
|
|
45
|
+
this.entries.set("/", this.newDirectory());
|
|
46
|
+
}
|
|
47
|
+
async readFile(targetPath) {
|
|
48
|
+
const entry = this.resolveEntry(targetPath);
|
|
49
|
+
if (!entry || entry.type !== "file") {
|
|
50
|
+
throw errnoError("ENOENT", `open '${targetPath}'`);
|
|
51
|
+
}
|
|
52
|
+
entry.atimeMs = Date.now();
|
|
53
|
+
return new Uint8Array(entry.data);
|
|
54
|
+
}
|
|
55
|
+
async readTextFile(targetPath) {
|
|
56
|
+
return new TextDecoder().decode(await this.readFile(targetPath));
|
|
57
|
+
}
|
|
58
|
+
async readDir(targetPath) {
|
|
59
|
+
return (await this.readDirWithTypes(targetPath)).map((entry) => entry.name);
|
|
60
|
+
}
|
|
61
|
+
async readDirWithTypes(targetPath) {
|
|
62
|
+
const resolved = this.resolvePath(targetPath);
|
|
63
|
+
const entry = this.entries.get(resolved);
|
|
64
|
+
if (!entry || entry.type !== "dir") {
|
|
65
|
+
throw errnoError("ENOENT", `scandir '${targetPath}'`);
|
|
66
|
+
}
|
|
67
|
+
const prefix = resolved === "/" ? "/" : `${resolved}/`;
|
|
68
|
+
const output = new Map();
|
|
69
|
+
for (const [entryPath, candidate] of this.entries) {
|
|
70
|
+
if (!entryPath.startsWith(prefix))
|
|
71
|
+
continue;
|
|
72
|
+
const rest = entryPath.slice(prefix.length);
|
|
73
|
+
if (!rest || rest.includes("/"))
|
|
74
|
+
continue;
|
|
75
|
+
output.set(rest, {
|
|
76
|
+
name: rest,
|
|
77
|
+
isDirectory: candidate.type === "dir",
|
|
78
|
+
isSymbolicLink: candidate.type === "symlink",
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return [...output.values()];
|
|
82
|
+
}
|
|
83
|
+
async writeFile(targetPath, content) {
|
|
84
|
+
const normalized = normalizePath(targetPath);
|
|
85
|
+
await this.mkdir(dirnameVirtual(normalized), { recursive: true });
|
|
86
|
+
const data = typeof content === "string"
|
|
87
|
+
? new TextEncoder().encode(content)
|
|
88
|
+
: new Uint8Array(content);
|
|
89
|
+
const existing = this.entries.get(normalized);
|
|
90
|
+
if (existing?.type === "file") {
|
|
91
|
+
existing.data = data;
|
|
92
|
+
existing.mtimeMs = Date.now();
|
|
93
|
+
existing.ctimeMs = Date.now();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
this.entries.set(normalized, {
|
|
98
|
+
type: "file",
|
|
99
|
+
data,
|
|
100
|
+
mode: S_IFREG | 0o644,
|
|
101
|
+
uid: 0,
|
|
102
|
+
gid: 0,
|
|
103
|
+
nlink: 1,
|
|
104
|
+
ino: nextInode++,
|
|
105
|
+
atimeMs: now,
|
|
106
|
+
mtimeMs: now,
|
|
107
|
+
ctimeMs: now,
|
|
108
|
+
birthtimeMs: now,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
async createDir(targetPath) {
|
|
112
|
+
const normalized = normalizePath(targetPath);
|
|
113
|
+
if (!this.entries.has(dirnameVirtual(normalized))) {
|
|
114
|
+
throw errnoError("ENOENT", `mkdir '${targetPath}'`);
|
|
115
|
+
}
|
|
116
|
+
if (!this.entries.has(normalized)) {
|
|
117
|
+
this.entries.set(normalized, this.newDirectory());
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async mkdir(targetPath, options) {
|
|
121
|
+
const normalized = normalizePath(targetPath);
|
|
122
|
+
if (options?.recursive === false) {
|
|
123
|
+
return this.createDir(normalized);
|
|
124
|
+
}
|
|
125
|
+
let current = "";
|
|
126
|
+
for (const part of normalized.split("/").filter(Boolean)) {
|
|
127
|
+
current += `/${part}`;
|
|
128
|
+
if (!this.entries.has(current)) {
|
|
129
|
+
this.entries.set(current, this.newDirectory());
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async exists(targetPath) {
|
|
134
|
+
try {
|
|
135
|
+
return this.entries.has(this.resolvePath(targetPath));
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async stat(targetPath) {
|
|
142
|
+
const entry = this.resolveEntry(targetPath);
|
|
143
|
+
if (!entry)
|
|
144
|
+
throw errnoError("ENOENT", `stat '${targetPath}'`);
|
|
145
|
+
return this.toStat(entry);
|
|
146
|
+
}
|
|
147
|
+
async removeFile(targetPath) {
|
|
148
|
+
const resolved = normalizePath(targetPath);
|
|
149
|
+
const entry = this.entries.get(resolved);
|
|
150
|
+
if (!entry || entry.type === "dir") {
|
|
151
|
+
throw errnoError("ENOENT", `unlink '${targetPath}'`);
|
|
152
|
+
}
|
|
153
|
+
this.entries.delete(resolved);
|
|
154
|
+
}
|
|
155
|
+
async removeDir(targetPath) {
|
|
156
|
+
const resolved = this.resolvePath(targetPath);
|
|
157
|
+
if (resolved === "/") {
|
|
158
|
+
throw errnoError("EPERM", "operation not permitted");
|
|
159
|
+
}
|
|
160
|
+
const entry = this.entries.get(resolved);
|
|
161
|
+
if (!entry || entry.type !== "dir") {
|
|
162
|
+
throw errnoError("ENOENT", `rmdir '${targetPath}'`);
|
|
163
|
+
}
|
|
164
|
+
const prefix = `${resolved}/`;
|
|
165
|
+
for (const key of this.entries.keys()) {
|
|
166
|
+
if (key.startsWith(prefix)) {
|
|
167
|
+
throw errnoError("ENOTEMPTY", `directory not empty '${targetPath}'`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
this.entries.delete(resolved);
|
|
171
|
+
}
|
|
172
|
+
async rename(oldPath, newPath) {
|
|
173
|
+
const oldResolved = this.resolvePath(oldPath);
|
|
174
|
+
const newResolved = normalizePath(newPath);
|
|
175
|
+
const entry = this.entries.get(oldResolved);
|
|
176
|
+
if (!entry)
|
|
177
|
+
throw errnoError("ENOENT", `rename '${oldPath}'`);
|
|
178
|
+
if (!this.entries.has(dirnameVirtual(newResolved))) {
|
|
179
|
+
throw errnoError("ENOENT", `rename '${newPath}'`);
|
|
180
|
+
}
|
|
181
|
+
if (entry.type !== "dir") {
|
|
182
|
+
this.entries.set(newResolved, entry);
|
|
183
|
+
this.entries.delete(oldResolved);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const prefix = `${oldResolved}/`;
|
|
187
|
+
const moved = [];
|
|
188
|
+
for (const candidate of this.entries) {
|
|
189
|
+
if (candidate[0] === oldResolved || candidate[0].startsWith(prefix)) {
|
|
190
|
+
moved.push(candidate);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
for (const [candidatePath] of moved) {
|
|
194
|
+
this.entries.delete(candidatePath);
|
|
195
|
+
}
|
|
196
|
+
for (const [candidatePath, candidate] of moved) {
|
|
197
|
+
const nextPath = candidatePath === oldResolved
|
|
198
|
+
? newResolved
|
|
199
|
+
: `${newResolved}${candidatePath.slice(oldResolved.length)}`;
|
|
200
|
+
this.entries.set(nextPath, candidate);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async realpath(targetPath) {
|
|
204
|
+
return this.resolvePath(targetPath);
|
|
205
|
+
}
|
|
206
|
+
async symlink(target, linkPath) {
|
|
207
|
+
const normalized = normalizePath(linkPath);
|
|
208
|
+
if (this.entries.has(normalized)) {
|
|
209
|
+
throw errnoError("EEXIST", `symlink '${linkPath}'`);
|
|
210
|
+
}
|
|
211
|
+
const now = Date.now();
|
|
212
|
+
this.entries.set(normalized, {
|
|
213
|
+
type: "symlink",
|
|
214
|
+
target,
|
|
215
|
+
mode: S_IFLNK | 0o777,
|
|
216
|
+
uid: 0,
|
|
217
|
+
gid: 0,
|
|
218
|
+
nlink: 1,
|
|
219
|
+
ino: nextInode++,
|
|
220
|
+
atimeMs: now,
|
|
221
|
+
mtimeMs: now,
|
|
222
|
+
ctimeMs: now,
|
|
223
|
+
birthtimeMs: now,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
async readlink(targetPath) {
|
|
227
|
+
const normalized = normalizePath(targetPath);
|
|
228
|
+
const entry = this.entries.get(normalized);
|
|
229
|
+
if (!entry || entry.type !== "symlink") {
|
|
230
|
+
throw errnoError("ENOENT", `readlink '${targetPath}'`);
|
|
231
|
+
}
|
|
232
|
+
return entry.target;
|
|
233
|
+
}
|
|
234
|
+
async lstat(targetPath) {
|
|
235
|
+
const entry = this.entries.get(normalizePath(targetPath));
|
|
236
|
+
if (!entry)
|
|
237
|
+
throw errnoError("ENOENT", `lstat '${targetPath}'`);
|
|
238
|
+
return this.toStat(entry);
|
|
239
|
+
}
|
|
240
|
+
async link(oldPath, newPath) {
|
|
241
|
+
const entry = this.resolveEntry(oldPath);
|
|
242
|
+
if (!entry || entry.type !== "file") {
|
|
243
|
+
throw errnoError("ENOENT", `link '${oldPath}'`);
|
|
244
|
+
}
|
|
245
|
+
const normalized = normalizePath(newPath);
|
|
246
|
+
if (this.entries.has(normalized)) {
|
|
247
|
+
throw errnoError("EEXIST", `link '${newPath}'`);
|
|
248
|
+
}
|
|
249
|
+
entry.nlink += 1;
|
|
250
|
+
this.entries.set(normalized, entry);
|
|
251
|
+
}
|
|
252
|
+
async chmod(targetPath, mode) {
|
|
253
|
+
const entry = this.resolveEntry(targetPath);
|
|
254
|
+
if (!entry)
|
|
255
|
+
throw errnoError("ENOENT", `chmod '${targetPath}'`);
|
|
256
|
+
const typeBits = mode & 0o170000;
|
|
257
|
+
entry.mode =
|
|
258
|
+
typeBits === 0 ? (entry.mode & 0o170000) | (mode & 0o7777) : mode;
|
|
259
|
+
entry.ctimeMs = Date.now();
|
|
260
|
+
}
|
|
261
|
+
async chown(targetPath, uid, gid) {
|
|
262
|
+
const entry = this.resolveEntry(targetPath);
|
|
263
|
+
if (!entry)
|
|
264
|
+
throw errnoError("ENOENT", `chown '${targetPath}'`);
|
|
265
|
+
entry.uid = uid;
|
|
266
|
+
entry.gid = gid;
|
|
267
|
+
entry.ctimeMs = Date.now();
|
|
268
|
+
}
|
|
269
|
+
async utimes(targetPath, atime, mtime) {
|
|
270
|
+
const entry = this.resolveEntry(targetPath);
|
|
271
|
+
if (!entry)
|
|
272
|
+
throw errnoError("ENOENT", `utimes '${targetPath}'`);
|
|
273
|
+
entry.atimeMs = atime;
|
|
274
|
+
entry.mtimeMs = mtime;
|
|
275
|
+
entry.ctimeMs = Date.now();
|
|
276
|
+
}
|
|
277
|
+
async truncate(targetPath, length) {
|
|
278
|
+
const entry = this.resolveEntry(targetPath);
|
|
279
|
+
if (!entry || entry.type !== "file") {
|
|
280
|
+
throw errnoError("ENOENT", `truncate '${targetPath}'`);
|
|
281
|
+
}
|
|
282
|
+
if (length < entry.data.length) {
|
|
283
|
+
entry.data = entry.data.slice(0, length);
|
|
284
|
+
}
|
|
285
|
+
else if (length > entry.data.length) {
|
|
286
|
+
const expanded = new Uint8Array(length);
|
|
287
|
+
expanded.set(entry.data);
|
|
288
|
+
entry.data = expanded;
|
|
289
|
+
}
|
|
290
|
+
entry.mtimeMs = Date.now();
|
|
291
|
+
entry.ctimeMs = Date.now();
|
|
292
|
+
}
|
|
293
|
+
async pread(targetPath, offset, length) {
|
|
294
|
+
const entry = this.resolveEntry(targetPath);
|
|
295
|
+
if (!entry || entry.type !== "file") {
|
|
296
|
+
throw errnoError("ENOENT", `open '${targetPath}'`);
|
|
297
|
+
}
|
|
298
|
+
if (offset >= entry.data.length)
|
|
299
|
+
return new Uint8Array(0);
|
|
300
|
+
return entry.data.slice(offset, Math.min(offset + length, entry.data.length));
|
|
301
|
+
}
|
|
302
|
+
async pwrite(targetPath, offset, data) {
|
|
303
|
+
const entry = this.resolveEntry(targetPath);
|
|
304
|
+
if (!entry || entry.type !== "file") {
|
|
305
|
+
throw errnoError("ENOENT", `open '${targetPath}'`);
|
|
306
|
+
}
|
|
307
|
+
const nextSize = Math.max(entry.data.length, offset + data.length);
|
|
308
|
+
const updated = new Uint8Array(nextSize);
|
|
309
|
+
updated.set(entry.data);
|
|
310
|
+
updated.set(new Uint8Array(data), offset);
|
|
311
|
+
entry.data = updated;
|
|
312
|
+
entry.mtimeMs = Date.now();
|
|
313
|
+
entry.ctimeMs = Date.now();
|
|
314
|
+
}
|
|
315
|
+
resolvePath(targetPath, depth = 0) {
|
|
316
|
+
if (depth > MAX_SYMLINK_DEPTH) {
|
|
317
|
+
throw errnoError("ELOOP", `too many symbolic links '${targetPath}'`);
|
|
318
|
+
}
|
|
319
|
+
const normalized = normalizePath(targetPath);
|
|
320
|
+
const entry = this.entries.get(normalized);
|
|
321
|
+
if (!entry)
|
|
322
|
+
return normalized;
|
|
323
|
+
if (entry.type === "symlink") {
|
|
324
|
+
const target = entry.target.startsWith("/")
|
|
325
|
+
? entry.target
|
|
326
|
+
: `${dirnameVirtual(normalized)}/${entry.target}`;
|
|
327
|
+
return this.resolvePath(target, depth + 1);
|
|
328
|
+
}
|
|
329
|
+
return normalized;
|
|
330
|
+
}
|
|
331
|
+
resolveEntry(targetPath) {
|
|
332
|
+
return this.entries.get(this.resolvePath(targetPath));
|
|
333
|
+
}
|
|
334
|
+
newDirectory() {
|
|
335
|
+
const now = Date.now();
|
|
336
|
+
return {
|
|
337
|
+
type: "dir",
|
|
338
|
+
mode: S_IFDIR | 0o755,
|
|
339
|
+
uid: 0,
|
|
340
|
+
gid: 0,
|
|
341
|
+
nlink: 2,
|
|
342
|
+
ino: nextInode++,
|
|
343
|
+
atimeMs: now,
|
|
344
|
+
mtimeMs: now,
|
|
345
|
+
ctimeMs: now,
|
|
346
|
+
birthtimeMs: now,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
toStat(entry) {
|
|
350
|
+
const size = entry.type === "file" ? entry.data.length : 4096;
|
|
351
|
+
return {
|
|
352
|
+
mode: entry.mode,
|
|
353
|
+
size,
|
|
354
|
+
blocks: size === 0 ? 0 : Math.ceil(size / 512),
|
|
355
|
+
dev: 1,
|
|
356
|
+
rdev: 0,
|
|
357
|
+
isDirectory: entry.type === "dir",
|
|
358
|
+
isSymbolicLink: entry.type === "symlink",
|
|
359
|
+
atimeMs: entry.atimeMs,
|
|
360
|
+
mtimeMs: entry.mtimeMs,
|
|
361
|
+
ctimeMs: entry.ctimeMs,
|
|
362
|
+
birthtimeMs: entry.birthtimeMs,
|
|
363
|
+
ino: entry.ino,
|
|
364
|
+
nlink: entry.nlink,
|
|
365
|
+
uid: entry.uid,
|
|
366
|
+
gid: entry.gid,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
export function createInMemoryFileSystem() {
|
|
371
|
+
return new InMemoryFileSystem();
|
|
372
|
+
}
|
|
373
|
+
function errnoError(code, message) {
|
|
374
|
+
return new KernelError(code, message);
|
|
375
|
+
}
|