hub-vaults.mjs
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd
feat(calendar): enforce agent context tiers in retrieval AP…
Human
minor
⚠ breaking
1 day ago
| 1 | /** |
| 2 | * Multi-vault (Phase 15): read/write data/hub_vaults.yaml. |
| 3 | * Format: { vaults: [ { id: string, path: string, label?: string } ] } |
| 4 | * At least one entry with id "default" when file exists. |
| 5 | */ |
| 6 | |
| 7 | import fs from 'fs'; |
| 8 | import path from 'path'; |
| 9 | import yaml from 'js-yaml'; |
| 10 | |
| 11 | const VAULTS_FILE = 'hub_vaults.yaml'; |
| 12 | |
| 13 | /** |
| 14 | * Read vault list from data_dir. Returns empty array if file missing or invalid. |
| 15 | * @param {string} dataDir - Resolved data_dir path |
| 16 | * @param {string} [cwd] - For resolving relative paths (default dataDir parent) |
| 17 | * @returns {{ id: string, path: string, label?: string }[]} |
| 18 | */ |
| 19 | export function readHubVaults(dataDir, cwd) { |
| 20 | if (!dataDir || typeof dataDir !== 'string') return []; |
| 21 | const p = path.join(dataDir, VAULTS_FILE); |
| 22 | if (!fs.existsSync(p)) return []; |
| 23 | try { |
| 24 | const raw = fs.readFileSync(p, 'utf8'); |
| 25 | const data = yaml.load(raw) || {}; |
| 26 | const list = Array.isArray(data.vaults) ? data.vaults : []; |
| 27 | const cwdResolve = cwd || path.dirname(path.dirname(dataDir)); |
| 28 | return list |
| 29 | .filter((v) => v && typeof v.id === 'string' && typeof v.path === 'string') |
| 30 | .map((v) => ({ |
| 31 | id: String(v.id).trim(), |
| 32 | path: path.isAbsolute(v.path) ? v.path : path.resolve(cwdResolve, v.path), |
| 33 | label: typeof v.label === 'string' ? v.label.trim() : undefined, |
| 34 | })); |
| 35 | } catch (_) { |
| 36 | return []; |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | /** |
| 41 | * Write hub_vaults.yaml. Validates that each path exists and is a directory. |
| 42 | * @param {string} dataDir - Resolved data_dir path |
| 43 | * @param {{ id: string, path: string, label?: string }[]} vaults |
| 44 | * @param {string} [cwd] - For resolving relative paths when writing (paths stored relative if under cwd) |
| 45 | */ |
| 46 | export function writeHubVaults(dataDir, vaults, cwd) { |
| 47 | if (!dataDir || typeof dataDir !== 'string') throw new Error('data_dir is required'); |
| 48 | if (!Array.isArray(vaults)) throw new Error('vaults must be an array'); |
| 49 | const cwdResolve = cwd || path.dirname(path.dirname(dataDir)); |
| 50 | const hasDefault = vaults.some((v) => v && String(v.id).trim() === 'default'); |
| 51 | if (!hasDefault) throw new Error('At least one vault must have id "default"'); |
| 52 | |
| 53 | const list = vaults.map((v) => { |
| 54 | const id = String(v.id).trim(); |
| 55 | if (!id) throw new Error('Vault id cannot be empty'); |
| 56 | const rawPath = String(v.path).trim(); |
| 57 | if (!rawPath) throw new Error(`Vault "${id}" path cannot be empty`); |
| 58 | const resolved = path.isAbsolute(rawPath) ? rawPath : path.resolve(cwdResolve, rawPath); |
| 59 | if (!fs.existsSync(resolved) || !fs.statSync(resolved).isDirectory()) { |
| 60 | throw new Error(`Vault path does not exist or is not a directory: ${resolved}`); |
| 61 | } |
| 62 | const storedPath = path.relative(cwdResolve, resolved); |
| 63 | return { |
| 64 | id, |
| 65 | path: storedPath.startsWith('..') ? resolved : storedPath, |
| 66 | ...(v.label != null && v.label !== '' ? { label: String(v.label).trim() } : {}), |
| 67 | }; |
| 68 | }); |
| 69 | |
| 70 | const out = yaml.dump({ vaults: list }, { lineWidth: 120 }); |
| 71 | fs.mkdirSync(dataDir, { recursive: true }); |
| 72 | fs.writeFileSync(path.join(dataDir, VAULTS_FILE), out, 'utf8'); |
| 73 | } |
File History
2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd
feat(calendar): enforce agent context tiers in retrieval AP…
Human
minor
⚠
1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6
docs: accept Calendar Events v0 spec with Phase 0 security …
Human
2 days ago