feat: add external plugin loader and examples for Brainy
Introduced `pluginLoader.ts` to enable loading and configuring augmentation plugins from external packages. Added `examples/externalPlugins.js` to demonstrate usage. Enhanced storage status reporting in `opfsStorage` and `BrainyData` with detailed storage capacity and usage methods. Updated README with an external plugin usage guide.
This commit is contained in:
parent
767c349f63
commit
b031a40b1c
11 changed files with 2220 additions and 1033 deletions
|
|
@ -451,4 +451,113 @@ export class FileSystemStorage implements StorageAdapter {
|
|||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about storage usage and capacity
|
||||
*/
|
||||
public async getStorageStatus(): Promise<{
|
||||
type: string;
|
||||
used: number;
|
||||
quota: number | null;
|
||||
details?: Record<string, any>;
|
||||
}> {
|
||||
await this.ensureInitialized()
|
||||
|
||||
try {
|
||||
// Calculate the total size of all files in the storage directories
|
||||
let totalSize = 0
|
||||
|
||||
// Helper function to calculate directory size
|
||||
const calculateDirSize = async (dirPath: string): Promise<number> => {
|
||||
let size = 0
|
||||
try {
|
||||
const files = await fs.promises.readdir(dirPath)
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(dirPath, file)
|
||||
const stats = await fs.promises.stat(filePath)
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
size += await calculateDirSize(filePath)
|
||||
} else {
|
||||
size += stats.size
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Error calculating size for ${dirPath}:`, error)
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// Calculate size for each directory
|
||||
const nodesDirSize = await calculateDirSize(this.nodesDir)
|
||||
const edgesDirSize = await calculateDirSize(this.edgesDir)
|
||||
const metadataDirSize = await calculateDirSize(this.metadataDir)
|
||||
|
||||
totalSize = nodesDirSize + edgesDirSize + metadataDirSize
|
||||
|
||||
// Get filesystem information
|
||||
let quota = null
|
||||
let details = {}
|
||||
|
||||
try {
|
||||
// Try to get disk space information
|
||||
const stats = await fs.promises.statfs(this.rootDir)
|
||||
if (stats) {
|
||||
const availableSpace = stats.bavail * stats.bsize
|
||||
const totalSpace = stats.blocks * stats.bsize
|
||||
|
||||
quota = totalSpace
|
||||
details = {
|
||||
availableSpace,
|
||||
totalSpace,
|
||||
freePercentage: (availableSpace / totalSpace) * 100
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Unable to get filesystem stats:', error)
|
||||
// If statfs is not available, try to use df command on Unix-like systems
|
||||
try {
|
||||
const { exec } = await import('child_process')
|
||||
const util = await import('util')
|
||||
const execPromise = util.promisify(exec)
|
||||
|
||||
const { stdout } = await execPromise(`df -k "${this.rootDir}"`)
|
||||
const lines = stdout.trim().split('\n')
|
||||
if (lines.length > 1) {
|
||||
const parts = lines[1].split(/\s+/)
|
||||
if (parts.length >= 4) {
|
||||
const totalKB = parseInt(parts[1], 10)
|
||||
const usedKB = parseInt(parts[2], 10)
|
||||
const availableKB = parseInt(parts[3], 10)
|
||||
|
||||
quota = totalKB * 1024
|
||||
details = {
|
||||
availableSpace: availableKB * 1024,
|
||||
totalSpace: totalKB * 1024,
|
||||
freePercentage: (availableKB / totalKB) * 100
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (dfError) {
|
||||
console.warn('Unable to get disk space using df command:', dfError)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'filesystem',
|
||||
used: totalSize,
|
||||
quota,
|
||||
details
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to get storage status:', error)
|
||||
return {
|
||||
type: 'filesystem',
|
||||
used: 0,
|
||||
quota: null,
|
||||
details: { error: String(error) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -498,6 +498,89 @@ export class OPFSStorage implements StorageAdapter {
|
|||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about storage usage and capacity
|
||||
*/
|
||||
public async getStorageStatus(): Promise<{
|
||||
type: string;
|
||||
used: number;
|
||||
quota: number | null;
|
||||
details?: Record<string, any>;
|
||||
}> {
|
||||
await this.ensureInitialized()
|
||||
|
||||
try {
|
||||
// Calculate the total size of all files in the storage directories
|
||||
let totalSize = 0
|
||||
|
||||
// Helper function to calculate directory size
|
||||
const calculateDirSize = async (dirHandle: FileSystemDirectoryHandle): Promise<number> => {
|
||||
let size = 0
|
||||
try {
|
||||
// @ts-ignore - TypeScript doesn't recognize FileSystemDirectoryHandle.entries() properly
|
||||
for await (const [name, handle] of dirHandle.entries()) {
|
||||
if (handle.kind === 'file') {
|
||||
const file = await handle.getFile()
|
||||
size += file.size
|
||||
} else if (handle.kind === 'directory') {
|
||||
size += await calculateDirSize(handle)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Error calculating size for directory:`, error)
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// Calculate size for each directory
|
||||
if (this.nodesDir) {
|
||||
totalSize += await calculateDirSize(this.nodesDir)
|
||||
}
|
||||
if (this.edgesDir) {
|
||||
totalSize += await calculateDirSize(this.edgesDir)
|
||||
}
|
||||
if (this.metadataDir) {
|
||||
totalSize += await calculateDirSize(this.metadataDir)
|
||||
}
|
||||
|
||||
// Get storage quota information using the Storage API
|
||||
let quota = null
|
||||
let details: Record<string, any> = {
|
||||
isPersistent: await this.isPersistent()
|
||||
}
|
||||
|
||||
try {
|
||||
if (navigator.storage && navigator.storage.estimate) {
|
||||
const estimate = await navigator.storage.estimate()
|
||||
quota = estimate.quota || null
|
||||
details = {
|
||||
...details,
|
||||
usage: estimate.usage,
|
||||
quota: estimate.quota,
|
||||
freePercentage: estimate.quota ? ((estimate.quota - (estimate.usage || 0)) / estimate.quota) * 100 : null
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Unable to get storage estimate:', error)
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'opfs',
|
||||
used: totalSize,
|
||||
quota,
|
||||
details
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to get storage status:', error)
|
||||
return {
|
||||
type: 'opfs',
|
||||
used: 0,
|
||||
quota: null,
|
||||
details: { error: String(error) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -693,6 +776,114 @@ export class MemoryStorage implements StorageAdapter {
|
|||
this.edges.clear()
|
||||
this.metadata.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about storage usage and capacity
|
||||
*/
|
||||
public async getStorageStatus(): Promise<{
|
||||
type: string;
|
||||
used: number;
|
||||
quota: number | null;
|
||||
details?: Record<string, any>;
|
||||
}> {
|
||||
try {
|
||||
// Estimate the size of data in memory
|
||||
let totalSize = 0
|
||||
|
||||
// Helper function to estimate object size in bytes
|
||||
const estimateSize = (obj: any): number => {
|
||||
if (obj === null || obj === undefined) return 0
|
||||
|
||||
const type = typeof obj
|
||||
|
||||
// Handle primitive types
|
||||
if (type === 'number') return 8
|
||||
if (type === 'string') return obj.length * 2
|
||||
if (type === 'boolean') return 4
|
||||
|
||||
// Handle arrays and objects
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.reduce((size, item) => size + estimateSize(item), 0)
|
||||
}
|
||||
|
||||
if (type === 'object') {
|
||||
if (obj instanceof Map) {
|
||||
let mapSize = 0
|
||||
for (const [key, value] of obj.entries()) {
|
||||
mapSize += estimateSize(key) + estimateSize(value)
|
||||
}
|
||||
return mapSize
|
||||
}
|
||||
|
||||
if (obj instanceof Set) {
|
||||
let setSize = 0
|
||||
for (const item of obj) {
|
||||
setSize += estimateSize(item)
|
||||
}
|
||||
return setSize
|
||||
}
|
||||
|
||||
// Regular object
|
||||
return Object.entries(obj).reduce(
|
||||
(size, [key, value]) => size + key.length * 2 + estimateSize(value),
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// Estimate size of nodes
|
||||
for (const node of this.nodes.values()) {
|
||||
totalSize += estimateSize(node)
|
||||
}
|
||||
|
||||
// Estimate size of edges
|
||||
for (const edge of this.edges.values()) {
|
||||
totalSize += estimateSize(edge)
|
||||
}
|
||||
|
||||
// Estimate size of metadata
|
||||
for (const meta of this.metadata.values()) {
|
||||
totalSize += estimateSize(meta)
|
||||
}
|
||||
|
||||
// Get memory information if available
|
||||
let quota = null
|
||||
let details: Record<string, any> = {
|
||||
nodesCount: this.nodes.size,
|
||||
edgesCount: this.edges.size,
|
||||
metadataCount: this.metadata.size
|
||||
}
|
||||
|
||||
// Try to get memory information if in a browser environment
|
||||
if (typeof window !== 'undefined' && (window as any).performance && (window as any).performance.memory) {
|
||||
const memory = (window as any).performance.memory
|
||||
quota = memory.jsHeapSizeLimit
|
||||
details = {
|
||||
...details,
|
||||
totalJSHeapSize: memory.totalJSHeapSize,
|
||||
usedJSHeapSize: memory.usedJSHeapSize,
|
||||
jsHeapSizeLimit: memory.jsHeapSizeLimit
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'memory',
|
||||
used: totalSize,
|
||||
quota,
|
||||
details
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to get memory storage status:', error)
|
||||
return {
|
||||
type: 'memory',
|
||||
used: 0,
|
||||
quota: null,
|
||||
details: { error: String(error) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue