aiwg
Version:
Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo
108 lines • 4.02 kB
JavaScript
/**
* Storage Adapter Registry
*
* `resolveStorage(subsystem)` is the consumer-facing entry point. Loads
* `.aiwg/storage.config` lazily, picks the configured backend per
* subsystem, and memoizes the adapter instance for the lifetime of the
* process.
*
* @design @.aiwg/architecture/storage-design.md
* @issue #934
* @issue #953
*/
import { loadStorageConfig, resolveSubsystemRoot } from './config.js';
import { FilesystemAdapter } from './backends/fs.js';
import { ObsidianAdapter } from './backends/obsidian.js';
import { LogseqAdapter } from './backends/logseq.js';
import { FortemiAdapter } from './backends/fortemi.js';
export { SUBSYSTEM_KEYS, BACKEND_TYPES, } from './types.js';
export { loadStorageConfig, storageConfigPath, validateStorageConfig, resolveSubsystemRoot, walkRejectingCredentials, FORBIDDEN_CREDENTIAL_KEYS, DEFAULT_SUBSYSTEM_ROOTS, } from './config.js';
export { FilesystemAdapter } from './backends/fs.js';
export { ObsidianAdapter } from './backends/obsidian.js';
export { LogseqAdapter } from './backends/logseq.js';
export { FortemiAdapter } from './backends/fortemi.js';
let state = null;
/**
* Initialize or reset the registry for a given project root. Most
* callers use the implicit lazy initialization triggered by
* `resolveStorage()`; explicit init is useful for tests and for the
* `aiwg storage` CLI.
*/
export async function initStorage(projectRoot = process.cwd()) {
const config = await loadStorageConfig(projectRoot);
state = {
projectRoot,
config,
adapters: new Map(),
loaded: true,
};
}
/** Reset memoized state. Test-only escape hatch. */
export function resetStorage() {
state = null;
}
/**
* Return the adapter for a subsystem. Loads `.aiwg/storage.config`
* lazily on first call.
*/
export async function resolveStorage(subsystem) {
if (!state || !state.loaded) {
await initStorage(process.cwd());
}
const s = state;
const cached = s.adapters.get(subsystem);
if (cached)
return cached;
const adapter = createAdapter(subsystem, s);
s.adapters.set(subsystem, adapter);
return adapter;
}
/**
* Return the loaded config (or null if `.aiwg/storage.config` does not
* exist). Triggers a load if not yet initialized. Used by the CLI.
*/
export async function getLoadedConfig(projectRoot = process.cwd()) {
if (!state || state.projectRoot !== projectRoot) {
await initStorage(projectRoot);
}
return state.config;
}
/**
* Build the adapter for a subsystem given the current registry state.
* Pure factory — no I/O beyond the path resolution the backend itself
* performs in its constructor.
*/
function createAdapter(subsystem, s) {
const backend = s.config?.backends?.[subsystem] ?? { type: 'fs' };
switch (backend.type) {
case 'fs': {
const root = resolveSubsystemRoot(subsystem, s.projectRoot, s.config);
return new FilesystemAdapter(root);
}
case 'obsidian':
return new ObsidianAdapter(backend);
case 'logseq':
return new LogseqAdapter(backend);
case 'fortemi':
return new FortemiAdapter({ subsystem, config: backend });
case 'notion':
case 'anythingllm':
case 's3':
case 'webdav': {
const stubIssues = {
notion: '#959',
anythingllm: '#960',
s3: '#962',
webdav: '#963',
};
throw new Error(`storage: backend "${backend.type}" is declared in storage.config but not yet implemented. ` +
`Tracked at ${stubIssues[backend.type]} — see https://git.integrolabs.net/roctinam/aiwg/issues/${stubIssues[backend.type].slice(1)} for status.`);
}
default: {
// Exhaustiveness check
const _exhaustive = backend;
throw new Error(`storage: unhandled backend type ${_exhaustive.type}`);
}
}
}
//# sourceMappingURL=index.js.map