@elizaos/plugin-local-storage 2.0.3-beta.6 → 2.0.3-beta.7
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/index.d.ts +6 -0
- package/dist/index.js +191 -0
- package/dist/index.js.map +12 -0
- package/dist/services/local-storage.d.ts +94 -0
- package/dist/types.d.ts +50 -0
- package/package.json +3 -3
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// src/services/local-storage.ts
|
|
2
|
+
import { promises as fsp } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { Storage } from "@brighter/storage-adapter-local";
|
|
6
|
+
import { logger, resolveStateDir, Service, ServiceType } from "@elizaos/core";
|
|
7
|
+
function resolveStorageRoot(runtime) {
|
|
8
|
+
const fromRuntime = runtime.getSetting("LOCAL_STORAGE_PATH");
|
|
9
|
+
if (typeof fromRuntime === "string" && fromRuntime.length > 0) {
|
|
10
|
+
return path.resolve(fromRuntime);
|
|
11
|
+
}
|
|
12
|
+
const fromEnv = process.env.LOCAL_STORAGE_PATH;
|
|
13
|
+
if (typeof fromEnv === "string" && fromEnv.length > 0) {
|
|
14
|
+
return path.resolve(fromEnv);
|
|
15
|
+
}
|
|
16
|
+
return path.join(resolveStateDir(), "attachments");
|
|
17
|
+
}
|
|
18
|
+
function joinKey(...segments) {
|
|
19
|
+
const joined = segments.filter((s) => typeof s === "string" && s.length > 0).join("/");
|
|
20
|
+
return joined.replace(/\/+/g, "/").replace(/^\/+/, "");
|
|
21
|
+
}
|
|
22
|
+
function normalizeStorageKey(...segments) {
|
|
23
|
+
for (const segment of segments) {
|
|
24
|
+
if (typeof segment === "string" && (path.isAbsolute(segment) || /^[A-Za-z]:/.test(segment))) {
|
|
25
|
+
throw new Error(`Invalid local storage key: ${segment}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const raw = joinKey(...segments).replace(/\\/g, "/");
|
|
29
|
+
const rawParts = raw.split("/").filter((part) => part.length > 0);
|
|
30
|
+
const normalized = path.posix.normalize(raw);
|
|
31
|
+
if (!raw || normalized === "." || normalized === ".." || normalized.startsWith("../") || rawParts.some((part) => part === "." || part === "..") || /^[A-Za-z]:/.test(raw)) {
|
|
32
|
+
throw new Error(`Invalid local storage key: ${raw || "<empty>"}`);
|
|
33
|
+
}
|
|
34
|
+
return normalized;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
class LocalFileStorageService extends Service {
|
|
38
|
+
static serviceType = ServiceType.REMOTE_FILES;
|
|
39
|
+
capabilityDescription = "Local filesystem attachment storage";
|
|
40
|
+
storage = null;
|
|
41
|
+
storageRoot = "";
|
|
42
|
+
static async start(runtime) {
|
|
43
|
+
logger.log("Initializing LocalFileStorageService");
|
|
44
|
+
const service = new LocalFileStorageService(runtime);
|
|
45
|
+
await service.initialize(runtime);
|
|
46
|
+
return service;
|
|
47
|
+
}
|
|
48
|
+
static async stop(runtime) {
|
|
49
|
+
const service = runtime.getService(ServiceType.REMOTE_FILES);
|
|
50
|
+
if (service) {
|
|
51
|
+
await service.stop();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async stop() {
|
|
55
|
+
this.storage = null;
|
|
56
|
+
}
|
|
57
|
+
get root() {
|
|
58
|
+
return this.storageRoot;
|
|
59
|
+
}
|
|
60
|
+
async initialize(runtime) {
|
|
61
|
+
this.storageRoot = resolveStorageRoot(runtime);
|
|
62
|
+
await fsp.mkdir(this.storageRoot, { recursive: true });
|
|
63
|
+
this.storage = Storage({ path: this.storageRoot });
|
|
64
|
+
}
|
|
65
|
+
getStorage() {
|
|
66
|
+
if (!this.storage) {
|
|
67
|
+
throw new Error("LocalFileStorageService not initialized");
|
|
68
|
+
}
|
|
69
|
+
return this.storage;
|
|
70
|
+
}
|
|
71
|
+
absolutePath(key) {
|
|
72
|
+
return path.join(this.storageRoot, key);
|
|
73
|
+
}
|
|
74
|
+
fileUrl(key) {
|
|
75
|
+
return pathToFileURL(this.absolutePath(key)).href;
|
|
76
|
+
}
|
|
77
|
+
async ensureKeyDir(key) {
|
|
78
|
+
const target = this.absolutePath(key);
|
|
79
|
+
await fsp.mkdir(path.dirname(target), { recursive: true });
|
|
80
|
+
}
|
|
81
|
+
async uploadFile(filePath, subDirectory) {
|
|
82
|
+
const storage = this.getStorage();
|
|
83
|
+
const baseFileName = `${Date.now()}-${path.basename(filePath)}`;
|
|
84
|
+
const key = normalizeStorageKey(subDirectory, baseFileName);
|
|
85
|
+
const buffer = await fsp.readFile(filePath);
|
|
86
|
+
await this.ensureKeyDir(key);
|
|
87
|
+
await storage.write(key, buffer, { encoding: "binary" });
|
|
88
|
+
return { success: true, url: this.fileUrl(key) };
|
|
89
|
+
}
|
|
90
|
+
async uploadBytes(data, fileName, contentType, subDirectory) {
|
|
91
|
+
const storage = this.getStorage();
|
|
92
|
+
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
93
|
+
const key = normalizeStorageKey(subDirectory, fileName);
|
|
94
|
+
await this.ensureKeyDir(key);
|
|
95
|
+
await storage.write(key, buffer, { encoding: "binary" });
|
|
96
|
+
return { success: true, url: this.fileUrl(key) };
|
|
97
|
+
}
|
|
98
|
+
async uploadJson(jsonData, fileName, subDirectory) {
|
|
99
|
+
if (!jsonData) {
|
|
100
|
+
return { success: false, error: "JSON data is required" };
|
|
101
|
+
}
|
|
102
|
+
const storage = this.getStorage();
|
|
103
|
+
const actualFileName = fileName ?? `${Date.now()}.json`;
|
|
104
|
+
const key = normalizeStorageKey(subDirectory, actualFileName);
|
|
105
|
+
const body = JSON.stringify(jsonData, null, 2);
|
|
106
|
+
await this.ensureKeyDir(key);
|
|
107
|
+
await storage.write(key, body, { encoding: "utf8" });
|
|
108
|
+
return { success: true, key, url: this.fileUrl(key) };
|
|
109
|
+
}
|
|
110
|
+
async downloadBytes(_unusedBucket, key) {
|
|
111
|
+
const storage = this.getStorage();
|
|
112
|
+
const safeKey = normalizeStorageKey(key);
|
|
113
|
+
const result = await storage.read(safeKey, { encoding: "binary" });
|
|
114
|
+
if (result === undefined) {
|
|
115
|
+
throw new Error(`Object not found: ${safeKey}`);
|
|
116
|
+
}
|
|
117
|
+
if (typeof result === "string") {
|
|
118
|
+
return Buffer.from(result, "binary");
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
async downloadFile(_unusedBucket, key, localPath) {
|
|
123
|
+
const buffer = await this.downloadBytes(_unusedBucket, key);
|
|
124
|
+
await fsp.writeFile(localPath, buffer);
|
|
125
|
+
}
|
|
126
|
+
async delete(_unusedBucket, key) {
|
|
127
|
+
const storage = this.getStorage();
|
|
128
|
+
await storage.remove(normalizeStorageKey(key));
|
|
129
|
+
}
|
|
130
|
+
async exists(_unusedBucket, key) {
|
|
131
|
+
const storage = this.getStorage();
|
|
132
|
+
try {
|
|
133
|
+
return await storage.exists(normalizeStorageKey(key));
|
|
134
|
+
} catch (err) {
|
|
135
|
+
const e = err;
|
|
136
|
+
if (e?.code === "ENOENT")
|
|
137
|
+
return false;
|
|
138
|
+
throw err;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async generateSignedUrl(fileName, _expiresIn) {
|
|
142
|
+
return this.fileUrl(normalizeStorageKey(fileName));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/types.ts
|
|
147
|
+
var CONTENT_TYPES = {
|
|
148
|
+
".png": "image/png",
|
|
149
|
+
".jpg": "image/jpeg",
|
|
150
|
+
".jpeg": "image/jpeg",
|
|
151
|
+
".gif": "image/gif",
|
|
152
|
+
".webp": "image/webp",
|
|
153
|
+
".pdf": "application/pdf",
|
|
154
|
+
".json": "application/json",
|
|
155
|
+
".txt": "text/plain",
|
|
156
|
+
".html": "text/html",
|
|
157
|
+
".css": "text/css",
|
|
158
|
+
".js": "application/javascript",
|
|
159
|
+
".mp3": "audio/mpeg",
|
|
160
|
+
".mp4": "video/mp4",
|
|
161
|
+
".wav": "audio/wav",
|
|
162
|
+
".webm": "video/webm"
|
|
163
|
+
};
|
|
164
|
+
function getContentType(filePath) {
|
|
165
|
+
const dot = filePath.lastIndexOf(".");
|
|
166
|
+
if (dot === -1)
|
|
167
|
+
return "application/octet-stream";
|
|
168
|
+
const ext = filePath.substring(dot).toLowerCase();
|
|
169
|
+
return CONTENT_TYPES[ext] ?? "application/octet-stream";
|
|
170
|
+
}
|
|
171
|
+
// src/index.ts
|
|
172
|
+
var localStoragePlugin = {
|
|
173
|
+
name: "local-storage",
|
|
174
|
+
description: "Local filesystem attachment storage (default fallback when Eliza Cloud storage is not connected)",
|
|
175
|
+
services: [LocalFileStorageService],
|
|
176
|
+
actions: [],
|
|
177
|
+
async dispose(runtime) {
|
|
178
|
+
const svc = runtime.getService(LocalFileStorageService.serviceType);
|
|
179
|
+
await svc?.stop();
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
var src_default = localStoragePlugin;
|
|
183
|
+
export {
|
|
184
|
+
localStoragePlugin,
|
|
185
|
+
getContentType,
|
|
186
|
+
src_default as default,
|
|
187
|
+
LocalFileStorageService,
|
|
188
|
+
CONTENT_TYPES
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
//# debugId=1AD964B3E06B65E264756E2164756E21
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/services/local-storage.ts", "../src/types.ts", "../src/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { promises as fsp } from \"node:fs\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { Storage } from \"@brighter/storage-adapter-local\";\nimport { type IAgentRuntime, logger, resolveStateDir, Service, ServiceType } from \"@elizaos/core\";\n\nimport type { JsonUploadResult, JsonValue, UploadResult } from \"../types\";\n\n/**\n * Subset of the @brighter/storage-adapter-local interface that this service\n * actually exercises. Typed locally to avoid leaking the upstream package's\n * loose `string | Buffer` return types into our public API.\n */\ninterface LocalStorage {\n write(path: string, data: Buffer | string, opts?: { encoding?: string }): Promise<void>;\n read(path: string, opts?: { encoding?: string }): Promise<Buffer | string | undefined>;\n exists(path: string): Promise<boolean>;\n remove(path: string, opts?: { recursive?: boolean }): Promise<void>;\n}\n\n/**\n * Resolves the storage root directory. Order of precedence:\n *\n * 1. `runtime.getSetting(\"LOCAL_STORAGE_PATH\")`\n * 2. `process.env.LOCAL_STORAGE_PATH`\n * 3. `<resolveStateDir()>/attachments`\n */\nfunction resolveStorageRoot(runtime: IAgentRuntime): string {\n const fromRuntime = runtime.getSetting(\"LOCAL_STORAGE_PATH\");\n if (typeof fromRuntime === \"string\" && fromRuntime.length > 0) {\n return path.resolve(fromRuntime);\n }\n const fromEnv = process.env.LOCAL_STORAGE_PATH;\n if (typeof fromEnv === \"string\" && fromEnv.length > 0) {\n return path.resolve(fromEnv);\n }\n return path.join(resolveStateDir(), \"attachments\");\n}\n\nfunction joinKey(...segments: Array<string | undefined>): string {\n const joined = segments\n .filter((s): s is string => typeof s === \"string\" && s.length > 0)\n .join(\"/\");\n return joined.replace(/\\/+/g, \"/\").replace(/^\\/+/, \"\");\n}\n\nfunction normalizeStorageKey(...segments: Array<string | undefined>): string {\n for (const segment of segments) {\n if (typeof segment === \"string\" && (path.isAbsolute(segment) || /^[A-Za-z]:/.test(segment))) {\n throw new Error(`Invalid local storage key: ${segment}`);\n }\n }\n const raw = joinKey(...segments).replace(/\\\\/g, \"/\");\n const rawParts = raw.split(\"/\").filter((part) => part.length > 0);\n const normalized = path.posix.normalize(raw);\n if (\n !raw ||\n normalized === \".\" ||\n normalized === \"..\" ||\n normalized.startsWith(\"../\") ||\n rawParts.some((part) => part === \".\" || part === \"..\") ||\n /^[A-Za-z]:/.test(raw)\n ) {\n throw new Error(`Invalid local storage key: ${raw || \"<empty>\"}`);\n }\n return normalized;\n}\n\n/**\n * Local filesystem implementation of `ServiceType.REMOTE_FILES`. Backed by\n * `@brighter/storage-adapter-local`. Method names mirror the surface that\n * the removed `@elizaos/plugin-s3-storage` `AwsS3Service` exposed so call\n * sites can be retargeted with no refactor.\n */\nexport class LocalFileStorageService extends Service {\n static override serviceType = ServiceType.REMOTE_FILES;\n capabilityDescription = \"Local filesystem attachment storage\";\n\n private storage: LocalStorage | null = null;\n private storageRoot = \"\";\n\n static override async start(runtime: IAgentRuntime): Promise<LocalFileStorageService> {\n logger.log(\"Initializing LocalFileStorageService\");\n const service = new LocalFileStorageService(runtime);\n await service.initialize(runtime);\n return service;\n }\n\n static async stop(runtime: IAgentRuntime): Promise<void> {\n const service = runtime.getService(ServiceType.REMOTE_FILES);\n if (service) {\n await service.stop();\n }\n }\n\n async stop(): Promise<void> {\n this.storage = null;\n }\n\n /**\n * Filesystem path to the storage root. Useful for tests and tooling.\n */\n get root(): string {\n return this.storageRoot;\n }\n\n private async initialize(runtime: IAgentRuntime): Promise<void> {\n this.storageRoot = resolveStorageRoot(runtime);\n await fsp.mkdir(this.storageRoot, { recursive: true });\n this.storage = Storage({ path: this.storageRoot });\n }\n\n private getStorage(): LocalStorage {\n if (!this.storage) {\n throw new Error(\"LocalFileStorageService not initialized\");\n }\n return this.storage;\n }\n\n private absolutePath(key: string): string {\n return path.join(this.storageRoot, key);\n }\n\n private fileUrl(key: string): string {\n // POSIX absolute paths produce `file:///foo/bar`; Windows absolute paths\n // need `file:///C:/foo/bar` (three slashes + drive letter + forward\n // slashes). `pathToFileURL` handles both correctly, including spaces\n // and other characters that need percent-encoding.\n return pathToFileURL(this.absolutePath(key)).href;\n }\n\n /**\n * Ensure every parent directory under the storage root exists before the\n * adapter writes to the leaf. `@brighter/storage-adapter-local` does\n * `fs.writeFile` directly — on Windows the intermediate dirs aren't\n * auto-created, so writes to `nested/dir/sample.bin` fail with ENOENT.\n */\n private async ensureKeyDir(key: string): Promise<void> {\n const target = this.absolutePath(key);\n await fsp.mkdir(path.dirname(target), { recursive: true });\n }\n\n /**\n * Copy a file from the filesystem into the storage root.\n *\n * @param filePath Source path on the local filesystem.\n * @param subDirectory Optional subdirectory under the storage root.\n */\n async uploadFile(filePath: string, subDirectory?: string): Promise<UploadResult> {\n const storage = this.getStorage();\n const baseFileName = `${Date.now()}-${path.basename(filePath)}`;\n const key = normalizeStorageKey(subDirectory, baseFileName);\n const buffer = await fsp.readFile(filePath);\n await this.ensureKeyDir(key);\n await storage.write(key, buffer, { encoding: \"binary\" });\n return { success: true, url: this.fileUrl(key) };\n }\n\n /**\n * Write raw bytes under a fixed key.\n *\n * @param data Bytes to write.\n * @param fileName Final segment of the storage key.\n * @param contentType Reserved for API parity with the previous S3\n * service. Local storage does not record per-object\n * content types beyond what the OS infers, so this\n * value is currently unused.\n * @param subDirectory Optional subdirectory under the storage root.\n */\n async uploadBytes(\n data: Buffer | Uint8Array,\n fileName: string,\n contentType: string,\n subDirectory?: string\n ): Promise<UploadResult> {\n const storage = this.getStorage();\n const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);\n const key = normalizeStorageKey(subDirectory, fileName);\n await this.ensureKeyDir(key);\n await storage.write(key, buffer, { encoding: \"binary\" });\n void contentType;\n return { success: true, url: this.fileUrl(key) };\n }\n\n /**\n * Serialize a JSON-shaped value and write it under a fixed key.\n *\n * @param jsonData The object to serialize.\n * @param fileName Optional filename. Defaults to `${Date.now()}.json`.\n * @param subDirectory Optional subdirectory under the storage root.\n */\n async uploadJson(\n jsonData: Record<string, JsonValue>,\n fileName?: string,\n subDirectory?: string\n ): Promise<JsonUploadResult> {\n if (!jsonData) {\n return { success: false, error: \"JSON data is required\" };\n }\n const storage = this.getStorage();\n const actualFileName = fileName ?? `${Date.now()}.json`;\n const key = normalizeStorageKey(subDirectory, actualFileName);\n const body = JSON.stringify(jsonData, null, 2);\n await this.ensureKeyDir(key);\n await storage.write(key, body, { encoding: \"utf8\" });\n return { success: true, key, url: this.fileUrl(key) };\n }\n\n /**\n * Read bytes for a previously-stored key.\n *\n * @param _unusedBucket Kept for API parity with the previous S3 service.\n * Local storage has no bucket concept; the value is\n * ignored and the key resolves under the storage\n * root.\n * @param key Storage key (relative path under the root).\n */\n async downloadBytes(_unusedBucket: string, key: string): Promise<Buffer> {\n const storage = this.getStorage();\n const safeKey = normalizeStorageKey(key);\n const result = await storage.read(safeKey, { encoding: \"binary\" });\n if (result === undefined) {\n throw new Error(`Object not found: ${safeKey}`);\n }\n if (typeof result === \"string\") {\n // Defensive: brighter local always returns Buffer when encoding is\n // 'binary', but the upstream type signature allows string. Keep the\n // public API a strict Buffer.\n return Buffer.from(result, \"binary\");\n }\n return result;\n }\n\n /**\n * Read bytes and write them to a local filesystem path.\n */\n async downloadFile(_unusedBucket: string, key: string, localPath: string): Promise<void> {\n const buffer = await this.downloadBytes(_unusedBucket, key);\n await fsp.writeFile(localPath, buffer);\n }\n\n /**\n * Remove a stored object. Idempotent: removing a missing key throws.\n */\n async delete(_unusedBucket: string, key: string): Promise<void> {\n const storage = this.getStorage();\n await storage.remove(normalizeStorageKey(key));\n }\n\n /**\n * Whether a stored object exists.\n */\n async exists(_unusedBucket: string, key: string): Promise<boolean> {\n const storage = this.getStorage();\n try {\n return await storage.exists(normalizeStorageKey(key));\n } catch (err: unknown) {\n // `@brighter/storage-adapter-local`'s `exists()` calls `fs.access`,\n // which throws ENOENT on Windows when the file is missing instead\n // of returning false. Coerce the absence case to `false` so the\n // public method behaves identically on every platform.\n const e = err as NodeJS.ErrnoException;\n if (e?.code === \"ENOENT\") return false;\n throw err;\n }\n }\n\n /**\n * Returns a `file://` absolute URL for the stored object.\n *\n * Local storage cannot mint short-lived signed URLs the way S3 can — the\n * URL is permanent and exposes the absolute filesystem path. Callers that\n * need a public, expiring URL should route attachment storage through\n * Eliza Cloud instead.\n *\n * @param fileName Storage key (relative path under the root).\n * @param _expiresIn Reserved for API parity with the previous S3 service.\n */\n async generateSignedUrl(fileName: string, _expiresIn?: number): Promise<string> {\n return this.fileUrl(normalizeStorageKey(fileName));\n }\n}\n\nexport default LocalFileStorageService;\n",
|
|
6
|
+
"/**\n * JSON-serializable primitive values.\n */\nexport type JsonPrimitive = string | number | boolean | null;\n\n/**\n * JSON-serializable object type.\n */\nexport interface JsonObject {\n [key: string]: JsonPrimitive | JsonObject | JsonArray;\n}\n\n/**\n * JSON-serializable array type.\n */\nexport type JsonArray = Array<JsonPrimitive | JsonObject | JsonArray>;\n\n/**\n * JSON-serializable value type.\n */\nexport type JsonValue = JsonPrimitive | JsonObject | JsonArray;\n\n/**\n * Result of a binary upload operation.\n *\n * Mirrors the shape returned by the removed `@elizaos/plugin-s3-storage`\n * `AwsS3Service` so callers can be retargeted without refactoring.\n */\nexport interface UploadResult {\n success: boolean;\n /** Absolute `file://` URL for the stored object on success. */\n url?: string;\n /** Human-readable error message on failure. */\n error?: string;\n}\n\n/**\n * Result of a JSON upload operation. Same as `UploadResult` plus the\n * resolved storage key.\n */\nexport interface JsonUploadResult extends UploadResult {\n /** Storage key (relative path under the storage root). */\n key?: string;\n}\n\n/**\n * Mapping from filename extension to MIME type. Mirrors the table that\n * shipped with the deprecated S3 plugin so behavior matches for callers\n * that depended on the original content-type inference.\n */\nexport const CONTENT_TYPES: Readonly<Record<string, string>> = {\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".pdf\": \"application/pdf\",\n \".json\": \"application/json\",\n \".txt\": \"text/plain\",\n \".html\": \"text/html\",\n \".css\": \"text/css\",\n \".js\": \"application/javascript\",\n \".mp3\": \"audio/mpeg\",\n \".mp4\": \"video/mp4\",\n \".wav\": \"audio/wav\",\n \".webm\": \"video/webm\",\n};\n\n/**\n * Resolve a Content-Type by extension. Falls back to\n * `application/octet-stream` when the extension is unknown.\n */\nexport function getContentType(filePath: string): string {\n const dot = filePath.lastIndexOf(\".\");\n if (dot === -1) return \"application/octet-stream\";\n const ext = filePath.substring(dot).toLowerCase();\n return CONTENT_TYPES[ext] ?? \"application/octet-stream\";\n}\n",
|
|
7
|
+
"import type { Plugin } from \"@elizaos/core\";\n\nimport { LocalFileStorageService } from \"./services/local-storage\";\n\nexport * from \"./types\";\nexport { LocalFileStorageService };\n\nexport const localStoragePlugin: Plugin = {\n name: \"local-storage\",\n description:\n \"Local filesystem attachment storage (default fallback when Eliza Cloud storage is not connected)\",\n services: [LocalFileStorageService],\n actions: [],\n async dispose(runtime) {\n const svc = runtime.getService<LocalFileStorageService>(LocalFileStorageService.serviceType);\n await svc?.stop();\n },\n};\n\nexport default localStoragePlugin;\n"
|
|
8
|
+
],
|
|
9
|
+
"mappings": "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",
|
|
10
|
+
"debugId": "1AD964B3E06B65E264756E2164756E21",
|
|
11
|
+
"names": []
|
|
12
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { type IAgentRuntime, Service } from "@elizaos/core";
|
|
2
|
+
import type { JsonUploadResult, JsonValue, UploadResult } from "../types";
|
|
3
|
+
/**
|
|
4
|
+
* Local filesystem implementation of `ServiceType.REMOTE_FILES`. Backed by
|
|
5
|
+
* `@brighter/storage-adapter-local`. Method names mirror the surface that
|
|
6
|
+
* the removed `@elizaos/plugin-s3-storage` `AwsS3Service` exposed so call
|
|
7
|
+
* sites can be retargeted with no refactor.
|
|
8
|
+
*/
|
|
9
|
+
export declare class LocalFileStorageService extends Service {
|
|
10
|
+
static serviceType: "aws_s3";
|
|
11
|
+
capabilityDescription: string;
|
|
12
|
+
private storage;
|
|
13
|
+
private storageRoot;
|
|
14
|
+
static start(runtime: IAgentRuntime): Promise<LocalFileStorageService>;
|
|
15
|
+
static stop(runtime: IAgentRuntime): Promise<void>;
|
|
16
|
+
stop(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Filesystem path to the storage root. Useful for tests and tooling.
|
|
19
|
+
*/
|
|
20
|
+
get root(): string;
|
|
21
|
+
private initialize;
|
|
22
|
+
private getStorage;
|
|
23
|
+
private absolutePath;
|
|
24
|
+
private fileUrl;
|
|
25
|
+
/**
|
|
26
|
+
* Ensure every parent directory under the storage root exists before the
|
|
27
|
+
* adapter writes to the leaf. `@brighter/storage-adapter-local` does
|
|
28
|
+
* `fs.writeFile` directly — on Windows the intermediate dirs aren't
|
|
29
|
+
* auto-created, so writes to `nested/dir/sample.bin` fail with ENOENT.
|
|
30
|
+
*/
|
|
31
|
+
private ensureKeyDir;
|
|
32
|
+
/**
|
|
33
|
+
* Copy a file from the filesystem into the storage root.
|
|
34
|
+
*
|
|
35
|
+
* @param filePath Source path on the local filesystem.
|
|
36
|
+
* @param subDirectory Optional subdirectory under the storage root.
|
|
37
|
+
*/
|
|
38
|
+
uploadFile(filePath: string, subDirectory?: string): Promise<UploadResult>;
|
|
39
|
+
/**
|
|
40
|
+
* Write raw bytes under a fixed key.
|
|
41
|
+
*
|
|
42
|
+
* @param data Bytes to write.
|
|
43
|
+
* @param fileName Final segment of the storage key.
|
|
44
|
+
* @param contentType Reserved for API parity with the previous S3
|
|
45
|
+
* service. Local storage does not record per-object
|
|
46
|
+
* content types beyond what the OS infers, so this
|
|
47
|
+
* value is currently unused.
|
|
48
|
+
* @param subDirectory Optional subdirectory under the storage root.
|
|
49
|
+
*/
|
|
50
|
+
uploadBytes(data: Buffer | Uint8Array, fileName: string, contentType: string, subDirectory?: string): Promise<UploadResult>;
|
|
51
|
+
/**
|
|
52
|
+
* Serialize a JSON-shaped value and write it under a fixed key.
|
|
53
|
+
*
|
|
54
|
+
* @param jsonData The object to serialize.
|
|
55
|
+
* @param fileName Optional filename. Defaults to `${Date.now()}.json`.
|
|
56
|
+
* @param subDirectory Optional subdirectory under the storage root.
|
|
57
|
+
*/
|
|
58
|
+
uploadJson(jsonData: Record<string, JsonValue>, fileName?: string, subDirectory?: string): Promise<JsonUploadResult>;
|
|
59
|
+
/**
|
|
60
|
+
* Read bytes for a previously-stored key.
|
|
61
|
+
*
|
|
62
|
+
* @param _unusedBucket Kept for API parity with the previous S3 service.
|
|
63
|
+
* Local storage has no bucket concept; the value is
|
|
64
|
+
* ignored and the key resolves under the storage
|
|
65
|
+
* root.
|
|
66
|
+
* @param key Storage key (relative path under the root).
|
|
67
|
+
*/
|
|
68
|
+
downloadBytes(_unusedBucket: string, key: string): Promise<Buffer>;
|
|
69
|
+
/**
|
|
70
|
+
* Read bytes and write them to a local filesystem path.
|
|
71
|
+
*/
|
|
72
|
+
downloadFile(_unusedBucket: string, key: string, localPath: string): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Remove a stored object. Idempotent: removing a missing key throws.
|
|
75
|
+
*/
|
|
76
|
+
delete(_unusedBucket: string, key: string): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Whether a stored object exists.
|
|
79
|
+
*/
|
|
80
|
+
exists(_unusedBucket: string, key: string): Promise<boolean>;
|
|
81
|
+
/**
|
|
82
|
+
* Returns a `file://` absolute URL for the stored object.
|
|
83
|
+
*
|
|
84
|
+
* Local storage cannot mint short-lived signed URLs the way S3 can — the
|
|
85
|
+
* URL is permanent and exposes the absolute filesystem path. Callers that
|
|
86
|
+
* need a public, expiring URL should route attachment storage through
|
|
87
|
+
* Eliza Cloud instead.
|
|
88
|
+
*
|
|
89
|
+
* @param fileName Storage key (relative path under the root).
|
|
90
|
+
* @param _expiresIn Reserved for API parity with the previous S3 service.
|
|
91
|
+
*/
|
|
92
|
+
generateSignedUrl(fileName: string, _expiresIn?: number): Promise<string>;
|
|
93
|
+
}
|
|
94
|
+
export default LocalFileStorageService;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON-serializable primitive values.
|
|
3
|
+
*/
|
|
4
|
+
export type JsonPrimitive = string | number | boolean | null;
|
|
5
|
+
/**
|
|
6
|
+
* JSON-serializable object type.
|
|
7
|
+
*/
|
|
8
|
+
export interface JsonObject {
|
|
9
|
+
[key: string]: JsonPrimitive | JsonObject | JsonArray;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* JSON-serializable array type.
|
|
13
|
+
*/
|
|
14
|
+
export type JsonArray = Array<JsonPrimitive | JsonObject | JsonArray>;
|
|
15
|
+
/**
|
|
16
|
+
* JSON-serializable value type.
|
|
17
|
+
*/
|
|
18
|
+
export type JsonValue = JsonPrimitive | JsonObject | JsonArray;
|
|
19
|
+
/**
|
|
20
|
+
* Result of a binary upload operation.
|
|
21
|
+
*
|
|
22
|
+
* Mirrors the shape returned by the removed `@elizaos/plugin-s3-storage`
|
|
23
|
+
* `AwsS3Service` so callers can be retargeted without refactoring.
|
|
24
|
+
*/
|
|
25
|
+
export interface UploadResult {
|
|
26
|
+
success: boolean;
|
|
27
|
+
/** Absolute `file://` URL for the stored object on success. */
|
|
28
|
+
url?: string;
|
|
29
|
+
/** Human-readable error message on failure. */
|
|
30
|
+
error?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Result of a JSON upload operation. Same as `UploadResult` plus the
|
|
34
|
+
* resolved storage key.
|
|
35
|
+
*/
|
|
36
|
+
export interface JsonUploadResult extends UploadResult {
|
|
37
|
+
/** Storage key (relative path under the storage root). */
|
|
38
|
+
key?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Mapping from filename extension to MIME type. Mirrors the table that
|
|
42
|
+
* shipped with the deprecated S3 plugin so behavior matches for callers
|
|
43
|
+
* that depended on the original content-type inference.
|
|
44
|
+
*/
|
|
45
|
+
export declare const CONTENT_TYPES: Readonly<Record<string, string>>;
|
|
46
|
+
/**
|
|
47
|
+
* Resolve a Content-Type by extension. Falls back to
|
|
48
|
+
* `application/octet-stream` when the extension is unknown.
|
|
49
|
+
*/
|
|
50
|
+
export declare function getContentType(filePath: string): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elizaos/plugin-local-storage",
|
|
3
|
-
"version": "2.0.3-beta.
|
|
3
|
+
"version": "2.0.3-beta.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@brighter/storage-adapter-local": "^1.6.3",
|
|
44
|
-
"@elizaos/core": "2.0.3-beta.
|
|
44
|
+
"@elizaos/core": "2.0.3-beta.7"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@biomejs/biome": "^2.4.14",
|
|
@@ -84,5 +84,5 @@
|
|
|
84
84
|
"node": "Node.js build available — uses node:fs / node:path"
|
|
85
85
|
}
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "61094f10458d11055c75b3dd0bae374e3f66bac5"
|
|
88
88
|
}
|