akm-cli
Version:
akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cur
147 lines (146 loc) • 5.86 kB
JavaScript
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
import { rethrowIfTestIsolationError } from "../../core/errors.js";
import { formatRegistryError } from "../../core/registry-url.js";
import { warn } from "../../core/warn.js";
import { closeDatabase, openIndexDatabase } from "./index-connection.js";
/**
* Upsert a registry index cache entry in index.db.
*
* @param db - Open index.db connection (from openDatabase / openExistingDatabase).
* @param registryUrl - Canonical URL of the registry (used as primary key).
* @param indexJson - Serialised registry index document (JSON string).
* @param opts.etag - HTTP ETag from the response (optional).
* @param opts.lastModified - HTTP Last-Modified from the response (optional).
*/
export function upsertRegistryIndexCache(db, registryUrl, indexJson, opts) {
db.prepare(`
INSERT INTO registry_index_cache (registry_url, fetched_at, etag, last_modified, index_json)
VALUES (?, ?, ?, ?, ?)
ON CONFLICT(registry_url) DO UPDATE SET
fetched_at = excluded.fetched_at,
etag = excluded.etag,
last_modified = excluded.last_modified,
index_json = excluded.index_json
`).run(registryUrl, new Date().toISOString(), opts?.etag ?? null, opts?.lastModified ?? null, indexJson);
}
/**
* Look up a cached registry index entry from index.db.
* Returns undefined when not found or when the entry is older than `maxAgeMs`.
*
* TTL check: if `Date.now() - new Date(fetched_at).getTime() > maxAgeMs` the
* entry is considered a cache miss and undefined is returned.
*
* @param db - Open index.db connection.
* @param registryUrl - Canonical URL of the registry (primary key).
* @param maxAgeMs - Maximum age in milliseconds before the entry is stale (default: 1 hour).
*/
export function getRegistryIndexCache(db, registryUrl, maxAgeMs = 3_600_000 /* 1 hour */) {
const row = db
.prepare(`SELECT fetched_at, etag, last_modified, index_json
FROM registry_index_cache WHERE registry_url = ?`)
.get(registryUrl);
if (!row)
return undefined;
const fetchedAt = Date.parse(row.fetched_at);
if (Number.isNaN(fetchedAt) || Date.now() - fetchedAt > maxAgeMs)
return undefined;
return { indexJson: row.index_json, etag: row.etag, lastModified: row.last_modified };
}
/**
* Open the cache DB (a failed open yields `db = undefined`; the bun-test
* isolation guard is re-thrown), run `fn`, and close the DB only after `fn`
* has fully settled — the callbacks are async, and closing earlier would tear
* the DB down mid-write.
*/
export async function withRegistryCacheDb(fn) {
let db;
try {
db = openIndexDatabase();
}
catch (err) {
rethrowIfTestIsolationError(err);
db = undefined;
}
try {
return await fn(db);
}
finally {
if (db) {
try {
closeDatabase(db);
}
catch {
/* ignore */
}
}
}
}
/**
* Returns a fresh cache hit when present, otherwise fetches live (writing the
* result back best-effort), and falls back to a stale cache row when the
* fetch fails.
*/
export async function fetchCachedJson(opts) {
const { cacheKey, ttlMs, parseCache, fetchFresh } = opts;
return withRegistryCacheDb(async (db) => {
let dbCacheResult;
try {
if (db) {
dbCacheResult = getRegistryIndexCache(db, cacheKey, ttlMs);
}
}
catch (err) {
// Never mask the bun-test isolation guard as "DB unavailable" — see
// rethrowIfTestIsolationError in src/core/errors.ts.
rethrowIfTestIsolationError(err);
// index.db read failed (pre-migration install or test env) — fall through
}
if (dbCacheResult) {
const cached = parseCache(dbCacheResult.indexJson, { stale: false });
if (cached !== undefined) {
return cached;
}
}
try {
const { value, cacheJson } = await fetchFresh();
if (db) {
try {
upsertRegistryIndexCache(db, cacheKey, cacheJson);
}
catch {
/* best-effort */
}
}
return value;
}
catch (err) {
// Fetch failed — use stale DB cache if available.
if (dbCacheResult) {
const stale = parseCache(dbCacheResult.indexJson, { stale: true });
if (stale !== undefined)
return stale;
}
// No in-TTL row — consult the cache PAST its TTL before giving up: a
// briefly unreachable registry should degrade to the last-known index,
// loudly, not hard-fail the command.
try {
const expiredRow = db ? getRegistryIndexCache(db, cacheKey, Number.POSITIVE_INFINITY) : undefined;
if (expiredRow) {
const stale = parseCache(expiredRow.indexJson, { stale: true });
if (stale !== undefined) {
warn(`Registry fetch failed (${formatRegistryError(err)}); ` +
"serving the last cached index, which is past its refresh interval.");
return stale;
}
}
}
catch (cacheErr) {
rethrowIfTestIsolationError(cacheErr);
// cache read failed — fall through to the original fetch error
}
throw err;
}
});
}