UNPKG

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

180 lines (179 loc) • 10.9 kB
// 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 { createHash } from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { adapterForId } from "../../core/adapter/registry.js"; import { parseBundleRef } from "../../core/asset/asset-ref.js"; import { isWithin } from "../../core/common.js"; import { loadConfig } from "../../core/config/config.js"; import { bundleComponentConfig, bundlesToSourceEntries } from "../../core/config/config-sources.js"; import { ConfigError, NotFoundError, UsageError } from "../../core/errors.js"; import { warnOnce } from "../../core/warn.js"; import { lookupBundleRef } from "../../indexer/indexer.js"; import { deriveInstallations } from "../../indexer/installations.js"; import { resolveEntryContentDir, resolveSourceEntries } from "../../indexer/search/search-source.js"; import { buildFileContext } from "../../indexer/walk/file-context.js"; async function defaultLookup(ref) { return lookupBundleRef(ref); } async function effectiveConfig(config) { if (config) return config; return loadConfig(); } function realDirectory(value, label) { let real; let stat; try { real = fs.realpathSync(value); stat = fs.statSync(real); } catch { throw new NotFoundError(`${label} is missing or unreadable. Rebuild the index and retry.`, "FILE_NOT_FOUND"); } if (!stat.isDirectory()) throw new ConfigError(`${label} is not a directory.`, "INVALID_CONFIG_FILE"); return real; } function realRegularFile(value, label) { let real; let stat; try { real = fs.realpathSync(value); stat = fs.statSync(real); } catch { throw new NotFoundError(`${label} is stale, missing, or unreadable. Rebuild the index and retry.`, "FILE_NOT_FOUND"); } if (!stat.isFile()) throw new NotFoundError(`${label} is not a regular file.`, "FILE_NOT_FOUND"); return real; } function isLexicallyWithin(candidate, root) { const relative = path.relative(path.resolve(root), path.resolve(candidate)); return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative)); } function implicitComponentForEntry(entry, config, realRoot) { const sources = resolveSourceEntries(undefined, config); const installations = deriveInstallations(sources); for (const [index, installation] of installations.entries()) { if (installation.id !== entry.bundleId) continue; const source = sources[index]; const component = installation.components[0]; if (!source || !component) continue; const canonicalRoot = realDirectory(component.root, `Canonical implicit source root for ${entry.bundleId}`); if (canonicalRoot !== realRoot) { warnOnce(`execution-source-root-drift:${entry.itemRef}`, `[command] Indexed root for ${JSON.stringify(entry.itemRef)} no longer matches the working bundle source; re-resolving from live config. Run \`akm index --full\` to refresh it.`); } if (component.adapter !== entry.adapterId) { warnOnce(`execution-source-adapter-drift:${entry.itemRef}`, `[command] Indexed adapter ${JSON.stringify(entry.adapterId)} for ${JSON.stringify(entry.itemRef)} no longer matches the working source's adapter ${JSON.stringify(component.adapter)}; re-resolving from live config. Run \`akm index --full\` to refresh it.`); } return Object.freeze({ id: installation.id, adapter: component.adapter, root: canonicalRoot, writable: component.writable, }); } return undefined; } function componentForEntry(entry, config, realRoot) { const configured = config.bundles?.[entry.bundleId]; if (!configured) { const implicit = implicitComponentForEntry(entry, config, realRoot); if (implicit) return implicit; throw new ConfigError(`Indexed execution source ${JSON.stringify(entry.itemRef)} belongs to unconfigured bundle ${JSON.stringify(entry.bundleId)}.`, "INVALID_CONFIG_FILE", "Refresh the bundle configuration and rebuild the index before dispatching this asset."); } const component = bundleComponentConfig(configured); const configuredAdapter = component?.adapter; if (configuredAdapter && configuredAdapter !== entry.adapterId) { warnOnce(`execution-source-adapter-drift:${entry.itemRef}`, `[command] Indexed adapter ${JSON.stringify(entry.adapterId)} for ${JSON.stringify(entry.itemRef)} no longer matches the bundle config's adapter ${JSON.stringify(configuredAdapter)}; re-resolving from live config. Run \`akm index --full\` to refresh it.`); } const configuredSource = bundlesToSourceEntries(config)?.find((source) => source.name === entry.bundleId); const configuredContentRoot = configuredSource ? resolveEntryContentDir(configuredSource) : undefined; if (!configuredSource || !configuredContentRoot) { warnOnce(`execution-source-unresolved:${entry.itemRef}`, `[command] Configured source for ${JSON.stringify(entry.itemRef)} no longer resolves to materialized content; dispatching from the indexed location instead. Run \`akm index --full\` after restoring or updating the bundle.`); } else { const lexicalSourceRoot = path.resolve(configuredContentRoot); const lexicalConfiguredRoot = path.resolve(configuredContentRoot, component?.root ?? "."); if (!isLexicallyWithin(lexicalConfiguredRoot, lexicalSourceRoot)) { throw new ConfigError(`Configured component root for ${JSON.stringify(entry.itemRef)} resolves outside its materialized bundle source.`, "INVALID_CONFIG_FILE", "Keep component roots inside their owning bundle source, then run `akm index --full`."); } const configuredSourceRoot = realDirectory(lexicalSourceRoot, `Configured source root for ${entry.bundleId}`); let configuredRoot; try { configuredRoot = realDirectory(lexicalConfiguredRoot, `Configured component root for ${entry.bundleId}`); } catch { throw new ConfigError(`Configured ${JSON.stringify(configuredSource.type)} source for ${JSON.stringify(entry.itemRef)} is not materialized at its current component root.`, "INVALID_CONFIG_FILE", "Restore or update the bundle, then run `akm index --full` before dispatching this asset."); } if (!isWithin(configuredRoot, configuredSourceRoot)) { throw new ConfigError(`Configured component root for ${JSON.stringify(entry.itemRef)} resolves outside its materialized bundle source.`, "INVALID_CONFIG_FILE", "Remove the escaping symlink or component path, then run `akm index --full`."); } if (configuredRoot !== realRoot) { warnOnce(`execution-source-root-drift:${entry.itemRef}`, `[command] Indexed root for ${JSON.stringify(entry.itemRef)} no longer matches the ${JSON.stringify(configuredSource.type)} bundle source; re-resolving from live config. Run \`akm index --full\` to refresh it.`); } } const writable = component?.writable ?? configured.writable ?? configured.path !== undefined; return Object.freeze({ id: entry.bundleId, adapter: entry.adapterId, root: realRoot, writable, }); } function expectedIndexedType(kind) { return kind === "command" ? "command" : "agent"; } function assertRenderedIdentity(source, entry, realRoot, realFile, content) { const expectedFile = path.relative(realRoot, realFile).replace(/\\/g, "/"); const expectedHash = createHash("sha256").update(content, "utf8").digest("hex"); if (source.identity.ref !== entry.itemRef || source.identity.bundle !== entry.bundleId || source.identity.adapter !== entry.adapterId || source.identity.file !== expectedFile || source.identity.hash !== expectedHash) { throw new ConfigError(`Execution renderer identity drift for ${JSON.stringify(entry.itemRef)}.`, "INVALID_CONFIG_FILE", "Run `akm index --full`; if the mismatch remains, repair the owning bundle adapter."); } } export async function loadAdapterExecutionSource(refInput, expectedKind, options = {}) { const parsed = parseBundleRef(refInput); if (parsed.fragment !== undefined) { throw new UsageError(`Execution source ${JSON.stringify(refInput)} cannot select a fragment.`, "INVALID_FLAG_VALUE"); } const entry = await (options.lookup ?? defaultLookup)(parsed); if (!entry) { throw new NotFoundError(`Execution source ${JSON.stringify(refInput)} was not found in the local index.`, "ASSET_NOT_FOUND", "Run `akm index --full`, then retry the command ref."); } const indexedType = expectedIndexedType(expectedKind); if (entry.type !== indexedType) { throw new UsageError(`Execution source ${JSON.stringify(refInput)} resolves to type ${JSON.stringify(entry.type)}, expected ${JSON.stringify(indexedType)}.`, "INVALID_FLAG_VALUE"); } const config = await effectiveConfig(options.config); const realRoot = realDirectory(entry.stashDir, `Indexed component root for ${entry.itemRef}`); const realFile = realRegularFile(entry.filePath, `Indexed execution source ${entry.itemRef}`); if (!isWithin(realFile, realRoot) || realFile === realRoot) { throw new ConfigError(`Indexed execution source ${JSON.stringify(entry.itemRef)} resolves outside its component root.`, "INVALID_CONFIG_FILE", "Remove the escaping path and rebuild the index."); } const component = componentForEntry(entry, config, realRoot); const adapter = (options.adapterFor ?? adapterForId)(entry.adapterId); if (!adapter) { throw new ConfigError(`Execution source adapter ${JSON.stringify(entry.adapterId)} is not registered.`, "INVALID_CONFIG_FILE"); } if (!adapter.renderExecutionSource) { throw new UsageError(`Bundle adapter ${JSON.stringify(entry.adapterId)} does not support command or persona execution.`, "INVALID_FLAG_VALUE"); } const fileContext = (options.fileContext ?? buildFileContext)(entry.stashDir, entry.filePath); const rendered = adapter.renderExecutionSource(component, fileContext); if (!rendered || rendered.kind !== expectedKind) { throw new ConfigError(`Bundle adapter ${JSON.stringify(entry.adapterId)} did not render ${JSON.stringify(entry.itemRef)} as a ${expectedKind}.`, "INVALID_CONFIG_FILE", "Rebuild the index; if the mismatch remains, repair the source file or owning adapter."); } assertRenderedIdentity(rendered, entry, realRoot, realFile, fileContext.content()); return rendered; }